FastAPI/Unvicorn中间件调用call_next时请求无规律挂起问题咨询
问题1答复
不能直接判定问题出自FastAPI而非Uvicorn/其他组件。
你观察到的call_next阶段挂起、路径操作函数首条日志未输出的现象,本质是await call_next(request)执行时,Starlette(FastAPI底层依赖的Web框架)需要先完成请求体全量读取、参数解析校验、路由匹配全流程,才会进入你定义的路径函数。这一阶段的链路涉及Uvicorn的请求IO处理、Starlette的Multipart表单解析逻辑、甚至上游Cortex网关的请求转发逻辑,任意环节出问题都会导致该阶段挂起。
问题2答复
可能的故障原因
- 上游请求传输不完整:Cortex.dev网关或客户端出现半开连接,请求头已经送达服务端(所以中间件pre请求日志正常打印),但请求体(尤其是你上传的图片数据)未完整传输,服务端一直在等待请求体数据,导致解析流程卡住。
- 旧版本依赖的已知bug:你当前使用的FastAPI/Starlette/Uvicorn版本存在Multipart表单/文件上传解析的已知bug,触发特定条件时会出现无限挂起。
- 事件循环被意外阻塞:如果在请求解析的依赖链中存在未异步化的同步阻塞IO操作(比如隐藏的同步文件读取、网络调用),会直接卡住整个async事件循环,导致所有该worker上的请求都无法继续处理。
- 大文件上传无超时:如果上传的图片体积过大,且没有配置请求体读取超时,会导致服务端长时间等待数据传输完成,表现为无规律挂起。
修复方案
- 先加请求监控定位问题:在中间件pre逻辑中新增
Content-Length请求头打印,对比正常请求和挂起请求的请求头差异,同时用tcpdump抓包确认挂起时请求体是否完整送达服务端,排除Cortex网关/客户端的传输问题。 - 升级核心依赖版本:将FastAPI、Starlette、Uvicorn全部升级到最新稳定版,规避已知的解析逻辑bug。
- 手动加请求读取超时:不使用FastAPI默认的
UploadFile+Form参数自动解析,改为在路径函数中手动读取请求体,并用asyncio.wait_for包裹读取逻辑,设置合理的超时时间(比如30秒),超时后直接抛出异常避免无限挂起,示例逻辑如下:
import asyncio @app.post("/") async def run(request: Request): try: cortexId = request.headers.get('x-request-id') print("Cortex ID: " + cortexId + " >>> 开始读取请求. Time stamp: " + str(datetime.now())) # 30秒超时读取表单内容 form = await asyncio.wait_for(request.form(), timeout=30) image = form.get("image") renderFactor = int(form.get("renderFactor", 12)) requestId = form.get("requestId", "-1") include_header = form.get("include_header", "bin") # 后续原有逻辑 except asyncio.TimeoutError: raise HTTPException(status_code=408, detail="请求读取超时")
- 避免事件循环阻塞:所有同步阻塞操作(哪怕是第三方库的调用)都不要直接在async函数中执行,用
asyncio.to_thread包裹扔到线程池运行,或者改成def类型的同步路径函数,FastAPI会自动把同步路径函数扔到线程池执行,不会阻塞事件循环。
内容的提问来源于stack exchange,提问作者Crashalot
相关产品推荐
相关产品推荐

