一个2018年才诞生的框架,凭什么用7年就超越统治了十几年的Django和Flask?答案藏在三个时代级的结构性变化里。
开场:一组让人沉默的数据
先别急着看"FastAPI是什么"。我们直接上数据。
根据JetBrains联合Python Software Foundation发布的2024年度Python开发者调查 ------覆盖近200个国家、超过3万名开发者------FastAPI的开发者使用率在一年内从29%跃升至38%,同比增长约31%,首次正式超越Django(35%)和Flask(34%),成为Python生态中使用率最高的Web框架。
GitHub上,FastAPI星标数已突破92k ,超过Flask(约71k)和Django(约86k)。超过一半的财富500强企业已在生产环境使用FastAPI,微软、Netflix、Uber都在列。而在AI领域,这个名单还要加上OpenAI、Anthropic、Cohere------也就是说,你每调用一次ChatGPT API或Claude API,背后大概率有FastAPI在处理请求。
这背后有一个关键拐点:2022年底ChatGPT发布。在那之前,FastAPI已经是增长很快的明星框架,但趋势还不算压倒性。AI浪潮来了之后,它的增长曲线直接从"快速上升"变成了"近乎垂直"。
一个2018年底才发布、满打满算才8岁的框架,凭什么在Django和Flask统治了十几年的Python生态里做到这件事?
这不是一个"FastAPI比别人更好"的简单故事。真正的答案是------它恰好踩中了这个时代三个结构性需求的交叉点:异步I/O从加分项变成标配、类型安全从可选变成必需、AI应用大规模爆发需要一个原生适配的API层。
接下来我们一个一个拆。
驱动力一:异步从"可选项"变成"标配"
要理解FastAPI的性能优势,得先搞懂它底下的ASGI协议到底解决了什么问题。
WSGI时代的天生缺陷
Django和Flask都是基于WSGI(Web Server Gateway Interface)协议的。WSGI是2003年制定的标准,在那个年代它非常优雅------一个请求进来,调用一个Python函数,返回一个响应,简单直接。
但它有一个致命的设计假设:每个请求都是同步阻塞的。什么意思?你的代码在等数据库返回、等外部API响应、等磁盘I/O的时候,整个处理线程就干等着,什么都做不了。
打个比方:WSGI模型就像一个餐厅只有一个服务员,他给第一桌点完菜之后,必须站在厨房门口等菜做好,才能去给第二桌点菜。厨房做菜那5分钟里,服务员完全空转。
这在2003年不算大问题------那时候的Web应用主要是"查数据库、渲染模板、返回HTML",I/O操作相对简单。但今天呢?一个AI应用的一次请求可能同时要做这些事:
- 调用Embedding模型生成向量(耗时100-300ms)
- 查向量数据库做相似度检索(耗时50-200ms)
- 调用LLM推理生成结果(耗时1-10秒,甚至更长)
- 流式返回token给前端
- 可能还要查用户信息、写日志、调用其他微服务
如果用同步框架处理这类请求,服务器资源会大量浪费在"等"上。
ASGI的本质:一个服务员同时服务所有桌
ASGI(Asynchronous Server Gateway Interface)是2018年前后成型的新标准。它用一个叫做"事件循环"的机制,让单个线程可以在I/O等待期间切换去处理其他请求。
还是那个餐厅的比喻:ASGI模式下,服务员点完第一桌菜之后,不需要在厨房门口傻等,他可以直接去给第二桌、第三桌点菜。等厨房喊"菜好了",他再回来端菜。一个服务员就能同时服务很多桌。
这不是玄学,是实打实的性能差距:
上图综合了TechEmpower基准测试和AWS云实测数据(2025-2026年)。注意一个关键规律:I/O操作越多、等待时间越长,FastAPI对同步框架的优势越明显。纯JSON序列化场景FastAPI快约8倍;到了含LLM调用的场景,因为单次请求耗时本来就长,绝对QPS都会下降,但FastAPI的并发优势依然存在------而且这还没算上流式响应带来的体验提升。
从代码层面看,差异也非常直观。
同步写法(Flask风格)------请求一来,线程就被占死了:
less
@app.route("/chat")
def chat():
# 这行阻塞当前线程,其他请求只能排队等着
response = requests.post("https://api.openai.com/v1/chat/completions", ...)
return response.json()
**异步写法(FastAPI风格)**------等待期间自动让出执行权:
@app.post("/chat")
async def chat(req: ChatRequest):
# await期间事件循环可以去处理其他请求
async with httpx.AsyncClient() as client:
response = await client.post(
"https://api.openai.com/v1/chat/completions",
json={"model": "gpt-4o", "messages": req.messages}
)
return response.json()
但是!异步 ≠ 自动高性能
这里必须讲一个非常重要的"反直觉"事实,也是新手最容易踩的坑:
如果你在async函数里调用了同步阻塞代码,事件循环会被整个卡住,异步的优势瞬间归零,甚至可能比同步还慢。
典型的错误写法:
python
@app.get("/bad-example")
async def bad_example():
# ❌ requests是同步库!这行会把整个事件循环堵死
r = requests.get("https://api.example.com/data")
# ❌ time.sleep也是同步阻塞
time.sleep(1)
# ❌ 同步ORM操作(SQLAlchemy同步Session
user = db.query(User).filter(User.id == 1).first()
return {"ok": True}
在这个反例里,你虽然写了async def,但里面全是阻塞调用,性能可能还不如直接用Flask------因为Flask至少还会用多线程来并行处理请求,而你把唯一的事件循环线程堵死了。
后面讲生产案例的时候,我们会看到一个真实踩坑的故事,非常典型。
驱动力二:类型安全从"给IDE看"变成"运行时约束"
Python的类型提示(Type Hints)从Python 3.5开始引入,但很长一段时间里,它的作用基本就是"给IDE补全用"、"给代码阅读者看"------运行时Python根本不会检查你传的类型对不对。
php
def add(a: int, b: int) -> int:
return a + b
add("hello", "world") # IDE会标红,但运行起来完全不报错
这在写小脚本时没问题,但在写API接口时就是灾难:前端传了一个字符串类型的user_id给你,你以为是整数,查数据库时直接报错;或者客户端漏传了一个必填字段,你直到深入业务逻辑才发现。
传统的解决方案是什么?每个接口手写一堆if "field" not in data: raise ValueError(...),或者用Marshmallow、DRF Serializer之类的库单独写一套schema。代码重复、文档脱节、维护成本高,改一个字段要改三处。
Pydantic做了一件事:把类型注解变成运行时契约
FastAPI基于Pydantic,做了一件极其优雅的事:你写的Python类型注解,就是数据校验规则,就是API文档,就是序列化/反序列化逻辑。一处定义,处处生效。
python
from fastapi import FastAPI
from pydantic import BaseModel, Field, field_validator
from datetime import datetime
from enum import Enum
app = FastAPI()
class Priority(str, Enum):
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
class CreateTodoRequest(BaseModel):
title: str = Field(..., min_length=1, max_length=200, description="待办标题")
priority: Priority = Field(default=Priority.MEDIUM, description="优先级")
due_date: datetime | None = Field(default=None, description="截止时间")
tags: list[str] = Field(default_factory=list, description="标签列表")
@field_validator("title")
@classmethod
def title_not_empty(cls, v: str) -> str:
if v.strip() == "":
raise ValueError("标题不能是纯空白")
return v.strip()
class TodoResponse(BaseModel):
id: int
title: str
priority: Priority
created_at: datetime
completed: bool = False
@app.post("/todos", response_model=TodoResponse, status_code=201)
async def create_todo(todo: CreateTodoRequest):
# 到这里,todo已经被自动校验、自动类型转换
# title一定是1-200个非空白字符
# priority一定是low/medium/high之一
# due_date如果传了就一定是合法datetime
new_todo = await todo_service.create(todo)
return new_todo
这段代码看起来平平无奇,但它同时完成了五件事:
- 自动解析请求体JSON:前端发来的JSON自动转成Python对象
- 自动类型校验:字段类型不对、长度超限、非法枚举值,通通返回结构化的422错误
- 自动类型转换 :前端传
"2026-09-21T10:00:00Z"自动转成datetime对象 - 自动序列化返回值:返回的ORM对象自动转成JSON,datetime自动转ISO格式
- 自动生成OpenAPI文档 :打开
/docs,Swagger UI已经有完整的接口文档,请求体示例、字段说明、响应格式一应俱全
Pydantic v2把核心校验逻辑用Rust重写了,性能比v1提升了5-50倍,这也是FastAPI在性能上能跟Go框架掰手腕的重要原因之一。
根据行业估算,类型校验和自动序列化能减少大约**40%**的常见API错误(漏字段、类型错、格式错)。更重要的是------文档永远跟代码同步。改了字段,文档自动更新,再也不会出现"文档是v1的,接口已经是v3的"这种惨剧。
驱动力三:AI时代的"原生适配"
如果说前两个驱动力------异步和类型安全------是"快和稳"的通用能力,那第三个驱动力才是FastAPI在2023年之后爆发式增长的真正加速器:它天然就是为AI应用设计的。
AI应用的典型模式是什么?
一个现代AI应用,不管是RAG问答、Agent工具调用、还是多模态生成,它的后端请求流几乎都是这样的:
erlang
接收用户请求
├─ 验证鉴权(DB查询)
├─ 生成query embedding(调用向量模型API
├─ 检索相关文档(向量数据库查询
├─ 可能调用多个工具(搜索API、代码执行、数据库...
├─ 构建Prompt调用LLM(流式响应可能持续几秒到几十秒)
└─ 一边生成一边流式返回token给前端(SSE / WebSocket
你发现没有?几乎每一步都是I/O操作 ,而且最后一步特别关键------用户期望的不是"加载10秒然后一下子出结果",而是像ChatGPT那样一个字一个字蹦出来的流式体验。
这正是FastAPI最擅长的场景。我们看一个真实可用的LLM流式接口:
python
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from typing import AsyncGenerator
import httpx
import json
app = FastAPI()
async def stream_llm_response(prompt: str) -> AsyncGenerator[str, None]:
"""调用LLM并以SSE格式逐token流式返回"""
async with httpx.AsyncClient(timeout=60.0) as client:
async with client.stream(
"POST",
"https://api.openai.com/v1/chat/completions",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"model": "gpt-4o-mini",
"messages": [{"role": "user", "content": prompt}],
"stream": True, # 开启流式
},
) as response:
async for line in response.aiter_lines():
if line.startswith("data: "):
data = line[6:]
if data == "[DONE]":
yield "data: [DONE]\n\n"
break
try:
chunk = json.loads(data)
token = chunk["choices"][0]["delta"].get("content", "")
if token:
# SSE格式:data: xxx\n\n
yield f"data: {json.dumps({'token': token})}\n\n"
except json.JSONDecodeError:
continue
@app.get("/chat/stream")
async def chat_stream(q: str):
"""流式聊天接口:前端通过EventSource接收"""
return StreamingResponse(
stream_llm_response(q),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"X-Accel-Buffering": "no", # 禁止Nginx缓冲
},
)
前端只需要十几行代码就能接:
ini
const es = new EventSource(`/chat/stream?q=${encodeURIComponent(question)}`);
es.onmessage = (e) => {
if (e.data === "[DONE]") { es.close(); return; }
const { token } = JSON.parse(e.data);
answerEl.textContent += token; // 逐字追加,体验跟ChatGPT一样
};
更进一步:异步并发多个I/O
更厉害的是,当你需要同时调用多个外部服务时,asyncio.gather可以让它们并行执行:
ini
@app.post("/rag-qa")
async def rag_qa(question: str):
# 三件事并行做,总耗时 ≈ 最慢的那一个
embedding_task = embed_model.aembed(question) # 生成向量
user_task = user_service.get_current_user() # 查用户信息
history_task = chat_history.get_recent(limit=10) # 查历史对话
embedding, user, history = await asyncio.gather(
embedding_task, user_task, history_task
)
# 然后并行查多个知识库
docs = await asyncio.gather(
vector_db.search(embedding, collection="faq"),
vector_db.search(embedding, collection="manuals"),
vector_db.search(embedding, collection="cases"),
)
all_docs = [d for col in docs for d in col]
return await llm.generate(question, context=all_docs, history=history)
在同步框架里,这些调用是串行的------总耗时是所有步骤相加。用FastAPI的异步模式,总耗时约等于最慢的那一步。对于一个要调用3-5个外部服务的AI应用,这可能是2秒和10秒的差距。
正是因为这种"原生适合",OpenAI的API网关、Anthropic的Claude API、Cohere的推理服务、HuggingFace的推理端点,底层都大量使用FastAPI。在国内,字节跳动、阿里等公司的AI服务也在向FastAPI迁移。
验证:真实生产案例,以及一个5倍性能的坑
数据和代码都是"理论上的"。我们来看一个真实的生产案例,看看踩坑和填坑的过程。
Shippo的账单平台:42 RPS → 210 RPS的故事
Shippo是一家做物流API的公司(Y Combinator毕业,融资过亿美元),他们的账单平台需要每天处理数十万个webhook回调------典型的高并发I/O场景。团队选择了FastAPI来构建这个服务。
一开始,压测结果让他们大跌眼镜:吞吐量只有42 RPS,延迟高得离谱,完全没体现出FastAPI的异步优势。
问题出在哪?团队排查后发现,他们在async路由里调用了一个同步的AWS SQS发送函数(当时用的boto3客户端是同步版)。就这一行,把整个事件循环堵死了,所有请求串行排队。
python
# ❌ 错误写法:async路由里直接调用同步boto3
@app.post("/webhook")
async def handle_webhook(payload: WebhookPayload):
# 这一行是同步阻塞,整个事件循环卡住
sqs_client.send_message(QueueUrl=QUEUE_URL, MessageBody=json.dumps(payload.dict()))
await process_webhook(payload)
return {"status": "ok"}
修复方式是用FastAPI/Starlette提供的run_in_threadpool,把同步调用扔到线程池里执行,不阻塞事件循环:
python
from fastapi.concurrency import run_in_threadpool
# ✅ 正确写法:同步阻塞代码交给线程池
@app.post("/webhook")
async def handle_webhook(payload: WebhookPayload):
await run_in_threadpool(
sqs_client.send_message,
QueueUrl=QUEUE_URL,
MessageBody=json.dumps(payload.model_dump())
)
await process_webhook(payload)
return {"status": "ok"}
更彻底的解决方案是直接换成异步SDK(比如aioboto3),让整个调用链都是异步的。改完之后,吞吐量从42 RPS提升到了210 RPS ------5倍提升。
这个案例的价值在于,它既说明了FastAPI的能力上限很高,也诚实地暴露了异步编程的学习门槛。FastAPI很快,但它不会自动帮你解决阻塞问题。 如果你不理解"什么代码会阻塞事件循环",写出来的异步服务可能比同步还慢。这不是框架的问题,是使用方式的问题。
其他采用案例
- Sophos(网络安全公司)的SecOps AI团队:用FastAPI构建AI驱动的安全分析服务,异步编排多个威胁检测模型。
- Boosted.ai:金融AI平台,用FastAPI支撑每天50,000+个AI投资研究工作流。
- Netflix内部工具:大量内部API服务用FastAPI构建,尤其是需要对接ML模型的场景。
横向对比:到底该选谁?
我不想写"FastAPI吊打一切"的文章。那是不负责任的营销文。我们诚实一点,把三个框架摆在台面上比一比:
| 维度 | FastAPI | Flask | Django |
|---|---|---|---|
| 底层协议 | ASGI(原生异步) | WSGI(同步) | WSGI/ASGI(3.1+支持异步) |
| 请求校验 | Pydantic自动校验 | 需手动/扩展 | DRF Serializer |
| API文档 | 原生Swagger/ReDoc | 需flask-restx/APIFairy | 需DRF + drf-yasg |
| 内置ORM | 无(自由选择) | 无 | Django ORM(成熟) |
| 后台Admin | 无(需sqladmin等) | 无 | Django Admin(业界标杆) |
| 异步能力 | 原生async/await | 2.0+支持但仍WSGI | 3.1+异步视图/4.1+异步ORM |
| 典型JSON QPS | ~100K+(纯序列化) | ~13K | ~8.5K |
| 适合场景 | API、AI服务、微服务 | 快速原型、小工具 | CMS、电商、后台管理系统 |
| 学习曲线 | 中等(需懂异步思维) | 低 | 较高(框架概念多) |
| 生态规模 | 快速增长中 | 庞大 | 最成熟 |
选型建议(直接给结论)
- 选FastAPI:如果你做的是API服务、AI/LLM应用、微服务、需要高并发I/O,或者前后端分离架构。这是它的甜蜜点。
- 选Django:如果你要做内容管理系统、电商后台、需要内置Admin和权限系统的企业级应用,或者团队需要一套强约定来保证项目一致性。Django的"全栈电池"在这些场景里仍然无可替代。
- 选Flask:如果你在做一个非常小的工具、内部脚本服务、快速原型验证,或者你需要最大程度的架构自由度。Flask的极简哲学依然有其价值。
一句话总结:FastAPI不是要取代Django和Flask,它是为"API-first + 异步I/O + 类型安全"这个交集里的场景提供了最优解。 恰好AI浪潮让这个交集变得越来越大。
边界:FastAPI不是银弹
最后,聊几个FastAPI不擅长、或者说你需要认真对待的事情。说清楚这些,才是对读者负责。
第一,异步编程的思维转变有真实成本。
习惯了同步编程的开发者,转到async/await模式需要理解事件循环、协程、Future、信号量这些概念。"不要阻塞事件循环"是一句简单的话,但要在几百个接口里都做到,需要训练和代码review。团队里如果有人在async函数里写了一句requests.get(),整个服务的性能就可能雪崩。
第二,FastAPI是"API-first"框架,不是"全栈"框架。
它不自带ORM(你可以选SQLAlchemy、Tortoise ORM、Prisma)、不自带Admin(可以用sqladmin)、不自带认证系统(要自己用OAuth2/JWT实现)、不自带模板引擎。这些都有成熟的第三方库,但你需要自己组装。对于喜欢"开箱即用"的团队,这会有初期选型成本。
第三,CPU密集型任务不要指望异步。
异步只能在I/O等待时帮你提高并发。如果你要做大量数学计算、图像处理、视频编码这类CPU密集任务,async不会让它变快。这种场景应该把任务丢给Celery/Arq任务队列,用多进程worker处理。
第四,多worker部署下全局变量不可靠。
用uvicorn --workers 4启动时是4个独立进程,内存不共享。如果你在内存里存了一个全局字典当缓存,不同worker之间看不到彼此的数据。需要缓存请用Redis。
第五,异步生态虽然成熟,但仍有缺口。
并不是所有Python库都有异步版本。最常用的数据库(SQLAlchemy 2.0 async、asyncpg、redis-py async)、HTTP客户端(httpx)都有了,但某些小众服务的SDK可能只有同步版,需要用run_in_threadpool包装。
结语:框架的崛起,是因为它回答了时代的问题
我们回头看技术演进的历史:
- Django在2005年崛起,因为它回答了"如何快速开发数据库驱动的新闻网站"这个问题。
- Flask在2010年崛起,因为它回答了"如何给开发者最大自由度"这个问题。
- FastAPI在2018年诞生、在2023-2025年爆发,因为它回答了三个 时代级问题:
- I/O密集型服务越来越多,同步模型撑不住了------它有ASGI和原生异步
- 前后端分离+团队协作规模越来越大,类型安全和文档同步是刚需------它有Pydantic和自动OpenAPI
- AI应用爆发,流式响应、多模型编排成为标准模式------它从底层就为此设计
FastAPI的作者Sebastián Ramírez在2018年创建这个项目时只有23岁。他当时的目标很朴素:"为什么Python的Web框架不能像Go一样快,同时保留Python的开发体验?"
他可能没想到,7年后他的框架会因为一场AI革命,成为整个Python生态增长最快的Web框架。
但这正是技术选择的有趣之处:最好的框架不一定是功能最多的,而是最恰好匹配当下需求的那个。 当你的应用需要同时调用embedding模型、向量数据库、LLM,并且要以流式方式把结果实时推给用户时------你会发现,FastAPI写出来的代码,就是这个场景下最自然、最高效的表达方式。
这不是营销。这是架构选型。
参考资料 & 延伸阅读
- JetBrains & PSF Python Developers Survey 2024 :jetbrains.com/resources/i...
- FastAPI官方文档 :fastapi.tiangolo.com
- Starlette文档 (FastAPI的ASGI底层):starlette.io
- Pydantic v2文档 :docs.pydantic.dev
- ASGI规范 :asgi.readthedocs.io
- Shippo工程博客:关于FastAPI高并发webhook处理的实践
- TechEmpower Web Framework Benchmarks :techempower.com/benchmarks
如果这篇文章对你有帮助,欢迎转发给正在做技术选型的同事。你在生产环境用过FastAPI吗?踩过什么有意思的坑?欢迎在评论区聊聊。
欢迎在公众号「鬼影的随笔日记」与我交流,那里有更多分享内容~