为什么我劝你别急着用 FastAPI?一个三线城市技术负责人的坦白局
上周五晚上十点半,办公室只剩我和运维老王还在对着日志抓头发。产品那边又临时加了个需求——要给内部运营平台接个 AI 文本生成接口,还得兼容现有的 Python 后端服务。我第一反应是:“这不就是个简单的 REST 接口吗?Flask 走起!”
结果第二天晨会,实习生小李弱弱地举手:“哥,我看 GitHub 上现在都用 FastAPI 了……”
我差点把咖啡喷出来。作为北京一家“三线互联网公司”(别问,问就是融资轮次卡在 B- 的尴尬期)的技术负责人,我日常最大的矛盾就是:一边想折腾新技术,一边被线上稳定性追着打。通勤一小时的疲惫还没散去,就得在“稳定压倒一切”和“别被时代抛弃”之间走钢丝。
但说实话,FastAPI 这玩意儿,真香。
从 Flask 到 FastAPI:不是升级,是降维打击?
我们团队之前清一色 Flask + Gunicorn + Nginx 套餐。代码写得飞起,文档靠口口相传,测试全靠 QA 暴力点。直到去年双11,一个没做输入校验的接口被前端传了个 null,直接把整个订单服务干趴了。那天晚上我蹲在机房角落,看着监控曲线像过山车,心里只有一个念头:类型系统救我狗命。
FastAPI 最打动我的,不是它快(虽然确实快),而是它把 Pydantic + OpenAPI + asyncio 三件套原生集成,让你写 API 时自动获得:
- 强类型参数校验
- 自动生成交互式文档(Swagger UI)
- 异步支持(不用自己造轮子)
对比一下主流 Python Web 框架:
| 特性 | Flask | Django REST | FastAPI |
|---|---|---|---|
| 自动 OpenAPI 文档 | ❌ (需插件) | ✅ (DRF-Swagger) | ✅ (内置) |
| Pydantic 支持 | ❌ | ⚠️ (需手动集成) | ✅ (深度整合) |
| 异步原生支持 | ⚠️ (Flask 2.0+) | ❌ | ✅ |
| 学习曲线 | ⭐ | ⭐⭐⭐ | ⭐⭐ |
| 生产稳定性 | ✅✅✅ | ✅✅✅ | ✅✅ (较新) |
注:我们公司内部评价标准——“上线后半夜被 PagerDuty 叫醒的次数”
看到没?FastAPI 在开发体验上简直是降维打击。但问题来了:我们这种小公司,敢用这么“新”的东西吗?
真实项目踩坑:从 Hello World 到扛住 500 QPS
第一步:别被“异步”忽悠了
很多新人一上来就狂写 async def,以为能榨干服务器性能。但现实很骨感——如果你的数据库驱动、Redis 客户端还是同步的,异步就是个寂寞。
我们第一次上线 FastAPI 时,DB 用的是 pymysql(同步库),结果异步接口反而比 Flask 慢了 30%。后来换成 asyncmy + SQLAlchemy 2.0 async,QPS 才真正起飞。
# 正确姿势:全链路异步
from fastapi import FastAPI
from sqlalchemy.ext.asyncio import create_async_engine
app = FastAPI()
engine = create_async_engine("mysql+asyncmy://user:pwd@host/db")
@app.get("/items/{item_id}")
async def read_item(item_id: int):
async with engine.connect() as conn:
result = await conn.execute(text("SELECT name FROM items WHERE id=:id"), {"id": item_id})
return {"name": result.scalar()}
吐槽:文心一言生成的示例代码里全是同步 DB 操作,差点害我翻车!
第二步:Pydantic 模型不是摆设
以前用 Flask,参数校验靠 if not request.json.get("name") 这种祖传代码。FastAPI 直接用 Pydantic 模型定义请求/响应结构,连文档都自动生成了。
from pydantic import BaseModel, validator
class GenerateRequest(BaseModel):
prompt: str
max_tokens: int = 100
@validator('max_tokens')
def check_max_tokens(cls, v):
if v > 500:
raise ValueError('Too greedy! Max 500 tokens')
return v
@app.post("/generate")
async def generate_text(req: GenerateRequest):
# 调用文心一言 API(伪代码)
response = await wenxin_api.generate(req.prompt, req.max_tokens)
return {"result": response.text}
上线后,前端再也没法传 max_tokens: "infinity" 这种骚操作了。产品经理看文档界面时眼睛都亮了:“这 Swagger 能直接测接口?!”
和 JavaScript 前端的“跨语言恋爱”
我们前端用 Vue3 + TypeScript,最头疼的就是接口字段对不齐。以前每次改后端字段,都要手动更新 Postman 集合,然后微信轰炸前端:“兄弟,user_name 改成 username 了啊!”
FastAPI 的 OpenAPI 文档直接成了前后端契约。前端甚至可以用 openapi-typescript 自动生成 TS 类型:
npx openapi-typescript http://localhost:8000/openapi.json -o src/api/types.ts
从此,后端改字段 → 提交代码 → CI 自动部署 → 前端拉取最新类型,一气呵成。再也不用在群里刷屏“接口改了!!!”,省下的时间够我多摸两条鱼。
GitHub 不是终点,生产环境才是考场
FastAPI 示例代码在 GitHub 上跑得飞起,但到了生产环境,坑才刚开始:
Gunicorn 工作模式必须用
uvicorn.workers.UvicornWorker
别再用默认的 sync worker 了,否则异步等于白写。gunicorn -k uvicorn.workers.UvicornWorker main:app -w 4 -b 0.0.0.0:8000中间件顺序很重要
我们曾把 CORS 中间件放错了位置,导致 OPTIONS 请求 404。正确顺序应该是:app.add_middleware(CORSMiddleware, ...) app.add_middleware(LoggingMiddleware) # 自定义日志中间件别信“零配置”
默认的 Swagger UI 没有认证,上线前记得关掉或加 Basic Auth:app = FastAPI(docs_url=None, redoc_url=None) # 完全禁用 # 或通过中间件加权限
最惨的是有一次,我把 DEBUG=True 忘记关了,结果错误堆栈直接暴露给用户。运维老王看到后,默默给我泡了杯枸杞茶:“年轻人,养生要紧。”
给纠结中的 Python 后端:我的建议
如果你和我一样,在一个小团队里既要快速迭代又要保证稳定,FastAPI 真的是甜点区选择:
- 小项目/新服务:闭眼选 FastAPI,开发效率提升 50%
- 老旧 Flask 系统:别急着重构!可以先用 FastAPI 写新模块,通过 Nginx 路由分流
- 高并发场景:确保全链路异步(DB/Cache/第三方 API),否则别吹 async
但记住:没有银弹。我们有个报表导出接口,因为要处理百万级数据,最后还是用 Celery + 后台任务搞定——FastAPI 只负责接收请求和返回任务 ID。
最后说点人话
写这篇文章的时候,窗外北京下着暴雨。想起三年前我还在用 Flask 手写 JSON Schema 校验,现在 FastAPI 一行注解搞定。技术演进真快,但工程师的核心能力从来不是追新,而是在约束条件下做最优解。
我们这种“三线公司”,没有大厂的资源冗余,每一次技术选型都是在走钢丝。FastAPI 让我能在 deadline 前交付功能的同时,睡个安稳觉——毕竟,自动生成的文档和类型校验,已经帮我挡掉了 80% 的低级 Bug。
所以下次实习生再问我:“哥,该学 FastAPI 吗?”
我会把键盘推给他:“先跑通这个例子,然后——帮我看下文心一言的 API 文档,产品说要加个‘AI 生成周报’功能……”
(完)
P.S. 本文代码已脱敏并上传至 GitHub 私有仓库,欢迎 star(并没有公开链接,别找了)
P.P.S. 通勤路上写的,如有错别字, blame 地铁 10 号线的晃动

评论 0