You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在线程中更优雅地运行Uvicorn?现有方案需优化

更优雅地在子线程中运行Uvicorn并实现正常关闭

你的问题确实戳中了Uvicorn在子线程运行的核心痛点——信号处理机制天生依赖主线程,直接禁用信号处理又会导致无法优雅关闭进程。你当前通过篡改__new__方法获取Server实例的方案虽然能实现需求,但属于侵入式的hack写法,容易和其他代码产生冲突,也不利于后续维护。

这里有个更优雅的实现思路:绕过uvicorn.run()便捷函数,手动构建Server实例并完全控制其生命周期,这样你能直接拿到Server对象,无需修改Uvicorn类的原生方法。

具体实现代码

import threading
import uvicorn
from uvicorn.config import Config

def run():
    """启动Uvicorn服务器并返回优雅关闭函数"""
    # 创建你的FastAPI/ASGI应用实例
    app = make_app()
    
    # 手动配置Uvicorn参数(按需调整host、port等)
    config = Config(app=app, host="127.0.0.1", port=8000)
    # 直接实例化Server对象
    server = uvicorn.Server(config)
    
    # 禁用Server自身的信号处理(信号只能在主线程生效,子线程中无用)
    server.install_signal_handlers = lambda *_, **__: None
    
    # 在子线程中启动服务器的服务循环
    server_thread = threading.Thread(target=server.serve)
    server_thread.start()
    
    def exit_server():
        print('exiting...')
        # 触发Uvicorn的优雅关闭流程
        server.handle_exit(None, None)
        # 等待子线程完全结束,避免残留进程
        server_thread.join()
    
    return exit_server

方案核心优势

  • 非侵入式:无需全局篡改Uvicorn类的方法,避免了未知的兼容性风险
  • 清晰可控:手动实例化Server,能明确把控服务器的配置、启动和关闭逻辑
  • 优雅可靠:通过调用原生的handle_exit方法触发正常关闭流程,不会出现强制终止导致的资源泄漏

扩展:主线程绑定信号处理(可选)

如果需要让程序响应系统信号(比如Ctrl+C终止),可以在主线程中注册信号处理器,调用返回的关闭函数:

import signal

# 启动服务器并获取关闭函数
exit_func = run()

def handle_system_signal(signum, frame):
    exit_func()

# 绑定SIGINT(Ctrl+C)和SIGTERM(系统终止信号)
signal.signal(signal.SIGINT, handle_system_signal)
signal.signal(signal.SIGTERM, handle_system_signal)

这个方案既解决了子线程中信号无法生效的问题,又能保证服务器优雅关闭,同时代码结构清晰、易维护。

内容的提问来源于stack exchange,提问作者Magnus Hokland Hegdahl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:58:50