原始的 run_mcp.py 脚本在使用 Ctrl+C (SIGINT) 终止时存在问题,会出现以下错误:
Fatal Python error: _enter_buffered_busy: could not acquire lock for <_io.BufferedReader name='<stdin>'> at interpreter shutdown, possibly due to daemon threads
该问题发生是因为:
- FastMCP 服务器创建了 WebSocket 客户端连接,使用了后台线程来处理 WebSocket 通信
- 当用户按下
Ctrl+C时,Python 解释器尝试关闭,但后台线程仍然在运行 - 这些后台线程(特别是 WebSocket 管理器的事件循环线程)没有被正确清理
- 导致在程序退出时出现资源竞争和锁获取失败
修复方案包括以下改进:
在 run_mcp.py 中添加了信号处理函数,用于捕获 SIGINT 和 SIGTERM 信号:
def signal_handler(sig, frame):
"""处理 Ctrl+C 信号,确保优雅关闭"""
print('\n正在关闭服务器...')
_cleanup_resources()
print('服务器已关闭')
sys.exit(0)
def setup_signal_handlers():
"""设置信号处理器以确保优雅关闭"""
signal.signal(signal.SIGINT, signal_handler) # Ctrl+C
signal.signal(signal.SIGTERM, signal_handler) # Termination添加了专门的清理函数来关闭 WebSocket 管理器和相关资源:
def _cleanup_resources():
"""清理资源,特别是 WebSocket 管理器"""
from magicapi_mcp.tool_registry import tool_registry
if tool_registry.context:
# 获取并关闭 WebSocket 管理器
try:
ws_manager = tool_registry.context.ws_manager
if ws_manager and hasattr(ws_manager, 'stop_sync'):
ws_manager.stop_sync()
print('WebSocket 管理器已关闭')
except Exception as e:
print(f'关闭 WebSocket 管理器时出错: {e}')
# 清理资源管理器
try:
resource_manager = tool_registry.context.resource_manager
if resource_manager and hasattr(resource_manager, 'close'):
resource_manager.close()
except Exception as e:
print(f'关闭资源管理器时出错: {e}')使用 atexit 模块注册退出时的清理函数:
import atexit
if __name__ == '__main__':
# 设置信号处理器
setup_signal_handlers()
# 注册退出时的清理函数
atexit.register(cleanup_on_exit)现在可以正常使用以下命令启动服务器:
uv run fastmcp run run_mcp.py:mcp要关闭服务器时,使用 Ctrl+C,服务器将:
- 接收信号
- 执行清理程序
- 关闭 WebSocket 连接
- 正常退出
- 修复后的代码同时添加到了
magicapi_mcp/magicapi_assistant.py文件中,这样无论是通过uv run fastmcp run run_mcp.py:mcp还是直接运行python run_mcp.py都能正确处理退出 - 所有的资源清理操作都使用了 try-catch 包装,以防止清理过程中出现异常影响正常退出
- WebSocket 管理器的
stop_sync()方法会同步地停止后台任务,确保所有线程安全关闭