应根据任务类型选择:i/o 密集型用 async def(需含 await),cpu 密集型或无 i/o 的简单逻辑用 def;混用时须将 cpu 任务交线程池执行,避免阻塞事件循环。

FastAPI 本身就能轻松扛住几千 QPS,关键不是“能不能”,而是你有没有踩进同步阻塞、依赖滥用、资源不复用这几个坑。
async def 和 def 到底该用哪个?
很多人以为所有路由都得写 async def,结果把纯计算逻辑也异步化,反而拖慢性能。事件循环不是万能调度器,它只擅长等 I/O —— 等数据库、等 HTTP 请求、等文件读写。CPU 密集型操作(比如 JSON 解析、图像处理、加密)会卡住整个循环。
- 用
async def:当函数里有await调用,比如database.fetch()、httpx.AsyncClient.get()、redis.execute() - 用普通
def:健康检查、简单字典返回、Pydantic 模型构造(无 I/O)、基础参数校验 - 混用场景:CPU 密集任务必须扔进线程池,用
loop.run_in_executor()或BackgroundTasks包裹,否则整个服务变卡顿
数据库连接为什么一并发就报错?
根本原因是用了同步驱动(如 psycopg2)或没配连接池。FastAPI 的异步能力在数据库层被直接废掉 —— 每个请求新建连接,几秒内就把连接数打满,报 too many clients already 或超时。
使用 font_manager.addfont() 添加中文字体文件,设置 rcParams['font.family'],并禁用 unicode_minus,使 matplotlib 显示中文。
- 必须换异步驱动:
asyncpg(PostgreSQL)、aiomysql(MySQL)、motor(MongoDB) - 连接池要复用:用
asyncpg.create_pool()初始化一次,全局单例或依赖注入缓存,别每次请求都connect() - 验证是否真异步:在路由里加
await asyncio.sleep(0),再压测;如果响应时间没随并发线性上涨,说明 I/O 层没阻塞
如何防止单点资源被挤爆?
限流不是可选项,是上线前必做的保命措施。靠前端或 Nginx 限流太晚,请求已进 Python 进程,内存和 CPU 可能已经吃紧。
- 用
asyncio.Semaphore控制临界资源并发数:比如第三方 API 调用限制为 10 QPS,就初始化semaphore = asyncio.Semaphore(10),所有调用前async with semaphore: - Redis 滑动窗口限流更准:适合按用户/IP 维度控制,用
ZADD+ZCOUNT+ZREMRANGEBYSCORE实现,避免固定窗口的突刺问题 - 别只限“请求数”:对上传大文件、导出报表这类长耗时接口,单独设低并发阈值(如 2),防止小请求被饿死
Uvicorn 启动参数怎么配才不翻车?
默认单 worker + 单 loop 就是玩具配置。生产环境不调参,等于裸奔。
-
--workers不是越多越好:建议设为2 × CPU 核数,再配合--limit-concurrency控制每 worker 最大连接数(如 100) - 必须加
--timeout-keep-alive 5:避免长连接堆积,尤其在 K8s Service 前有 LB 时 - 禁用
--reload上生产:inotify 监听开销大,且热重载会破坏连接池和全局状态 - 若需更高吞吐,用 Gunicorn + Uvicorn 组合:
gunicorn -k uvicorn.workers.UvicornWorker,利用多进程隔离内存压力
真正难的不是写 async,而是判断哪一行代码在偷偷阻塞事件循环 —— 一个 time.sleep(1)、一个没 await 的 session.commit()、一个没关的 httpx.Client(),都可能让整个服务在高并发下突然失速。上线前务必用 locust 或 hey 压测,盯着 uvicorn.access 日志里的响应时间和错误码看。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










