FastAPI异步接口在Gunicorn+Uvicorn Docker环境下同步运行问题排查
异步FastAPI应用在Gunicorn+Uvicorn部署下无法异步运行的修复方案
问题背景
采用Gunicorn作为进程管理器、Uvicorn Worker运行异步FastAPI应用,部署在Docker容器中,但含await asyncio.sleep(10)的异步接口表现为串行处理,而非预期的并发执行。开发环境直接用Uvicorn运行时异步功能正常。
相关代码与配置
FastAPI端点代码
@router.get("/info", status_code=status.HTTP_200_OK, response_model=InfoReturn) async def info(): main_logger.info("BEFORE INFO") await asyncio.sleep(10) main_logger.info("AFTER INFO") return { "version": __VERSION__, "description": "Application for conquering the world !" }
Dockerfile配置
FROM python:3.11.2-alpine3.17 RUN mkdir -p /opt/app && mkdir -p /etc/app VOLUME ["/etc/app"] WORKDIR /opt/app COPY requirements.txt /opt/app RUN set -e; \ apk update && apk add --no-cache --virtual .build-deps \ gcc \ libc-dev \ musl-dev \ linux-headers \ python3-dev \ libffi-dev \ openssl-dev \ ; \ pip install --upgrade pip; \ pip install --ignore-installed --no-cache-dir -r requirements.txt; \ pip install "psycopg[binary,pool]"; \ apk add vim; \ apk --purge del .build-deps; COPY ["." , "/opt/app"] CMD ["gunicorn", "run:app", "--workers", "4", "--worker-class", "uvicorn.workers.UvicornWorker", "--bind", "0.0.0.0:8000"]
问题现象
容器中运行时,并发请求/info接口会串行处理,日志输出如下:
2023-03-14 13:37:38|testing_api|INFO|BEFORE INFO 2023-03-14 13:47:38|testing_api|INFO|AFTER INFO 2023-03-14 13:47:38|testing_api|INFO|BEFORE INFO 2023-03-14 13:57:38|testing_api|INFO|AFTER INFO
预期的异步并发日志应为:
2023-03-14 13:37:38|testing_api|INFO|BEFORE INFO 2023-03-14 13:37:38|testing_api|INFO|BEFORE INFO 2023-03-14 13:47:38|testing_api|INFO|AFTER INFO 2023-03-14 13:47:38|testing_api|INFO|AFTER INFO
修复方案
1. 修正测试方式(最常见原因)
浏览器会对同域名、同路径的GET请求进行排队限制(如Chrome默认限制同域名并发连接数为6,且重复GET请求会串行发送),导致接口看似串行处理。改用无并发限制的工具测试:
- 终端用
curl并发发送:curl http://localhost:8000/info & curl http://localhost:8000/info - 使用Postman批量请求功能,或多个独立窗口同时发送请求
- 用Python脚本并发测试:
import asyncio import aiohttp async def fetch(session, url): async with session.get(url) as response: return await response.json() async def main(): async with aiohttp.ClientSession() as session: tasks = [fetch(session, "http://localhost:8000/info") for _ in range(2)] results = await asyncio.gather(*tasks) print(results) if __name__ == "__main__": asyncio.run(main())
2. 确认依赖版本兼容性
确保requirements.txt包含兼容版本的依赖:
fastapi>=0.95.0 uvicorn>=0.22.0 gunicorn>=20.1.0
旧版本Uvicorn Worker可能存在异步处理Bug,更新到稳定版本可解决问题。
3. 调整Gunicorn Worker配置
- 确保使用正确的异步Worker类:
uvicorn.workers.UvicornWorker(禁止使用同步Worker) - 可增加每个Worker的并发连接数(Uvicorn默认每个Worker处理100个并发连接),修改Dockerfile的CMD命令:
CMD ["gunicorn", "run:app", "--workers", "4", "--worker-class", "uvicorn.workers.UvicornWorker", "--bind", "0.0.0.0:8000", "--worker-connections", "1000"]
4. 确认应用的异步设置
检查run.py中FastAPI实例创建是否正确,确保未意外使用同步模式:
from fastapi import FastAPI app = FastAPI() # 默认支持异步,无需额外配置 # 导入路由 from app.routes import router app.include_router(router)
验证
按上述方案调整后,重新构建Docker镜像并运行,用正确的测试工具发送并发请求,即可看到日志输出符合预期的异步并发模式。
内容的提问来源于stack exchange,提问作者Syrious
相关产品推荐
相关产品推荐

