Digital Ocean应用平台上挂载FastAPI的Flask应用出现请求死锁超时问题
看起来你碰到了gunicorn同步worker模式下的典型死锁问题!我之前帮朋友排查过几乎一模一样的场景,咱们来拆解下原因和解决办法:
问题根源分析
你本地用waitress没问题,但部署到DO用gunicorn就出问题,核心原因在于:
- gunicorn默认使用同步(sync)worker,每个worker同一时间只能处理一个请求。
- 当你的Flask路由
/person/viewpersons发起内部HTTP请求到同一应用里的FastAPI接口/api/v1/relationship/type/时,这个请求会被卡在队列里——因为当前worker正忙着处理Flask的请求,根本腾不出手来响应自己发起的API调用。 - 直到gunicorn触发worker超时(默认30秒),把这个卡住的worker杀掉重启,队列里的API请求才被新启动的worker处理,所以你会看到之后API返回200的日志。
可行的解决方案
1. 增加gunicorn的worker数量
最简单的临时解决办法是启动gunicorn时指定多个worker,比如:
gunicorn --workers 2 your_app:app
这样当一个worker在处理Flask的请求时,另一个worker可以响应内部的API调用,不会出现死锁。注意要根据你DO实例的CPU核心数调整,一般建议workers = CPU核心数 * 2 + 1,避免资源过载。
2. 直接调用FastAPI逻辑,跳过HTTP请求
这是更高效也更彻底的解决方式——既然Flask和FastAPI是挂载在同一个应用里的(应该用了Werkzeug的DispatcherMiddleware吧?),完全不需要通过HTTP请求来调用API,直接导入FastAPI的视图函数或者业务逻辑即可:
# 假设你的FastAPI实例定义在fastapi_app.py里 from fastapi_app import app as fastapi_app from fastapi.testclient import TestClient # 在Flask路由中直接调用FastAPI接口逻辑 @app.route('/person/viewpersons') def viewpersons(): # 用TestClient模拟调用FastAPI,本质是直接执行视图函数,不走网络 client = TestClient(fastapi_app) response = client.get("/relationship/type/") # 处理response数据,生成Flask响应 data = response.json() return render_template('persons.html', relationship_types=data)
如果能直接导入FastAPI的视图函数(比如from your_routers import get_relationship_types),那就更直接了,连TestClient都不用,直接调用函数拿数据就行。
3. 改用异步worker模式
如果你的应用适合异步场景,可以换成gunicorn的异步worker,比如gevent或eventlet,这类worker可以同时处理多个请求,不会因为内部请求阻塞:
# 先安装gevent pip install gevent # 用gevent worker启动gunicorn gunicorn -k gevent --workers 1 your_app:app
注意如果用了requests库,可能需要打补丁让它兼容gevent,或者换成异步HTTP库比如aiohttp。
结合你的日志验证
从你提供的日志里能完美对应这个问题:
- worker 15在处理
/person/viewpersons时发起了API请求,自己被卡住 - 30秒后触发WORKER TIMEOUT,worker 15被杀死退出
- 新worker 17启动,处理之前挂起的
/api/v1/relationship/type/请求,返回200
按上面的方案调整后,应该就能解决这个死锁问题了!
备注:内容来源于stack exchange,提问作者Huck Wach

