FastAPI中自定义Middleware搭配StreamingResponse出现504网关超时问题
解决FastAPI自定义LoggingMiddleware搭配StreamingResponse导致Nginx 504超时问题
核心问题分析
自定义日志中间件大概率是破坏了StreamingResponse的流式传输特性:这类响应依赖分块编码(Transfer-Encoding: chunked)实时返回数据,若中间件试图缓存、读取完整响应体,或阻塞了响应流的传递,会导致Nginx长时间等待完整响应触发超时;而FileResponse是一次性返回完整文件内容,不会触发这类冲突。
具体修复方案
跳过StreamingResponse的响应体读取
很多日志中间件会强制读取完整响应体用于日志,但这会彻底阻塞流式传输。修改中间件,仅对非流式响应做体内容记录:async def dispatch(self, request: Request, call_next): response = await call_next(request) # 针对StreamingResponse仅记录响应类型,不读取body if isinstance(response, StreamingResponse): self.logger.info(f"Streaming response sent: {request.url.path}") else: # 非流式响应正常读取body日志 body = await response.body() self.logger.info(f"Response body: {body.decode()}") return response保留StreamingResponse的原始响应头
流式响应依赖Transfer-Encoding: chunked等特定头,若中间件随意修改或覆盖响应头,会导致Nginx无法识别流式传输。确保中间件仅对非流式响应做头修改:async def dispatch(self, request: Request, call_next): response = await call_next(request) if not isinstance(response, StreamingResponse): # 仅给非流式响应添加自定义头 response.headers["X-Logged"] = "true" # 流式响应直接返回,不修改头 return response移除中间件中的同步阻塞操作
若日志中间件包含同步IO(如同步写入文件、耗时的日志格式化),会拖慢流式响应的发送速度。替换为异步日志操作:# 使用异步日志库示例(如structlog异步绑定器) import structlog from structlog.stdlib import AsyncBoundLogger logger: AsyncBoundLogger = structlog.get_logger() async def dispatch(self, request: Request, call_next): response = await call_next(request) # 异步记录日志,避免阻塞响应流 await logger.ainfo( "request_processed", path=str(request.url), response_type=type(response).__name__ ) return response临时适配Nginx配置(辅助验证)
若需快速验证中间件修复效果,可临时调整Nginx的超时设置,但核心仍需修复中间件逻辑:location / { proxy_pass http://your_fastapi_upstream; proxy_http_version 1.1; proxy_set_header Connection ""; # 适配长连接流式传输 proxy_read_timeout 300s; # 适当延长超时窗口 }
内容的提问来源于stack exchange,提问作者Emad Helmi
相关产品推荐
相关产品推荐

