01 FastAPI 介绍 —— 为什么它值得你花时间学习?
关键词: FastAPI, ASGI, 现代 Web 框架
难度: ★★☆☆☆
场景导入:你的后端代码,还能更简洁吗?
你有没有经历过这种场景:团队要快速出一个 API 服务,功能不复杂,但你却花了大量时间在拼装参数校验、手写 OpenAPI 文档、处理各种序列化问题。用 Flask?它灵活,但类型校验和文档得靠一堆第三方库缝缝补补。用 Django?功能强大,可对于纯 API 场景又显得笨重。
FastAPI 走了一条不同的路。它把 Python 的类型注解当作骨架,同时解决了请求解析、数据序列化和接口文档自动生成这三件事。用它来搭建一个图书管理 API 这种中等规模的服务,代码量少得让人舒心。
这个系列会带着你从零开始构建一个图书管理 API,从框架特性一直聊到路由分层、依赖注入、ORM 集成这些工程化实践。今天是第一篇,我们先搞清楚三个问题:FastAPI 到底是什么?它的性能从哪来?一个最简服务长什么样?
原理解析:FastAPI 快在哪?先看分层
FastAPI 是一个用来构建 API 的现代 Python Web 框架。名字里藏着两层意思:API 是它的定位——接收 HTTP 请求、返回结构化数据;Fast 则强调它在异步 I/O 和类型校验两端的开销控制。
它并没有重新发明轮子,而是在两个强大的库之上做了一层极其贴合开发者的封装:底层用 Starlette 处理请求循环,数据层靠 Pydantic 完成建模与校验。当你的服务收到一个 HTTP 请求时,它要穿过这样一条处理链:
(图注:各层的职责边界很清晰,请求自上而下流入业务函数。)
如果再放大到线上部署环境,加上 Nginx 和 ASGI 服务器(比如 Uvicorn),一次完整的调用链会是这样:
(图注:从客户端到响应返回,在真实部署中要经过多重接力。)
理解这些分层有两个直接的好处。第一,排查性能问题时你会有清晰的地图:是 Uvicorn 的事件循环慢了?还是 Pydantic 的校验耗时太多?各层独立,排查起来有条不紊。第二,迁移路径非常灵活:不满意 Uvicorn?可以换 Granian。觉得 Starlette 的中间件不够用?可以上纯 ASGI 应用。每一层都能被替换,不会把你锁死。
那么,它的性能优势到底来自哪里?主要就三块:
ASGI 异步协议:事件循环在等待数据库返回结果时,可以去处理其他请求,并发能力直接上了一个台阶。
Pydantic v2 的 Rust 内核:核心校验与序列化逻辑跑在
pydantic-core上,用 Rust 实现,比纯 Python 路径快得多。启动期的预处理:路由和依赖的元数据在应用启动时就分析完毕,请求来了直接匹配,不用再反复解析装饰器。
当然,真实吞吐量还是取决于你的业务 I/O 强度、部署方式和测试条件,拿到手后最好用自己的场景做一次基准测试,心里有数。
代码实现:5 分钟跑起来
光说不练假把式。下面是一个最简的 FastAPI 服务,提供了一个健康检查接口,并把后续要展开的图书端点用占位符写好了。
# main.py
from fastapi import FastAPI
app = FastAPI(
title="图书管理 API",
version="0.1.0",
description="贯穿本系列 20 章的示例 API,涵盖图书、作者、借阅三类资源。",
)
@app.get("/health")
async def health() -> dict:
return {"status": "ok"}
@app.get("/books")
async def list_books() -> list:
return []
@app.get("/books/{book_id}")
async def get_book(book_id: int) -> dict:
return {"id": book_id, "title": "placeholder"}
这里有几个细节很值得留意:
FastAPI()构造器里的那些元信息,并不是摆设,它们会原封不动地出现在自动生成的 OpenAPI 文档里。@app.get装饰器把路径和 HTTP 方法注册进了内部路由表,跟 Flask 的感觉很像,但背后的机制更强。book_id: int这个类型注解是真正干活的:FastAPI 会自动把路径参数转成整数,如果前端传了个abc过来,它会毫不留情地返回一个清晰的校验错误,完全不用你手动写判断。
把上面的文件保存好,用 uvicorn main:app --reload 启动,然后打开浏览器访问 http://127.0.0.1:8000/docs,你会看到一个现成的 Swagger 交互文档。再访问 /openapi.json,一份完整的 OpenAPI 3.1 描述已经安静地躺在那儿了。文档零配置生成,这在 FastAPI 里是出厂自带。
避坑指南 & 性能背后的代价
FastAPI 虽好,但用起来也有几个常见的坑,提前知道能少走弯路。
同步阻塞是性能杀手
如果你在def函数里干了一件耗时的同步操作(比如直接调 requests 库),整个事件循环都会被它卡住,单进程的吞吐量会直线下降。遇到实在绕不开的阻塞 I/O,记得用run_in_threadpool把它放到线程池里去跑。类型注解别偷懒
参数类型写成Any或者裸dict,等于亲手关掉了 Pydantic 的自动校验。这会让代码退化到手写校验的原始时代,FastAPI 的核心优势被你主动放弃了。全局状态要小心
开发时用--reload模式,app对象可能会被多次实例化。数据库连接池、Redis 客户端这类东西,千万别丢在全局作用域里,应该放在lifespan上下文管理器里初始化,避免进程里留下脏状态。异步栈得统一
一旦上了 aiomysql 或 SQLAlchemy 异步驱动,数据库相关的所有 handler 就都得用async def。同步的 Session 此时已不再适用,迁移时记得一次性改完,别混用。
面试官:你说 FastAPI 快,到底快在哪?
如果面试被问到这个问题,你可以这样拆解回答:
FastAPI 的“快”是多个层面的结合。
它基于 ASGI 异步协议,事件循环在 I/O 等待期间可以调度其他请求,这对高并发 API 场景特别关键。其次,Pydantic v2 用了 Rust 写的 pydantic-core 做校验和序列化,告别了纯 Python 的性能瓶颈。最后,框架在启动时就把路由和依赖分析好了,运行时几乎零开销。
从源码也能印证这点:Starlette.__call__ 是 ASGI 入口,APIRoute 在注册阶段就完成了所有准备工作,BaseModel.model_validate 则负责高速校验。对于咱们的图书管理 API 这种频繁读写数据库的场景,异步 I/O 带来的并发收益最明显。但如果你的服务主要是跑 CPU 密集型计算,光靠异步就不够了,还得配合进程池或者任务队列。
小结 & 下篇预告
今天我们把 FastAPI 的定位、技术栈分层和一个最简服务梳理了一遍。下一篇文章《02 项目结构》会以这个单文件为起点,带你规划一个可维护的工程目录,聊聊路由、业务逻辑和数据访问这三层如何各司其职。别错过哦。