FastAPI真香警告:别再用Flask写新项目了!

FastAPI跑起来
2025-12-29 15:19
阅读 1000

上周五晚上十一点,我坐在公司空荡荡的办公室里,盯着屏幕上最后一行部署成功的日志,长舒一口气。这可能是我在这家待了三年半的公司提交的最后一个PR了——下个月我就要离职创业,彻底告别“天天救火”的后端生活。

说起这个项目,其实挺有意思。本来是给内部运营团队搞个数据看板,结果产品经理临时加需求,说要支持实时查询上亿条订单数据。我心想:得,又是那种“简单做个接口就行”的经典话术。不过这次我没用熟悉的Flask,而是直接上了FastAPI。为什么?因为性能——这个词在我脑子里已经刻进DNA了。

从Flask到FastAPI:不是叛变,是进化

说实话,在这三年多时间里,Flask几乎成了我的右手。写API、搭微服务、做原型验证,它都稳如老狗。但随着业务量上来,特别是去年双11期间我们那个破接口QPS飙到5000+的时候,我第一次感受到了Python同步框架的天花板。

当时的情况是这样的:用户请求进来,查数据库,处理逻辑,返回结果。看起来平平无奇,但在高并发下,每个请求都要等前一个释放GIL(Global Interpreter Lock),系统CPU明明没跑满,响应时间却蹭蹭往上涨。运维同事在群里@我说:“哥,服务又雪崩了,监控报警都快炸了!”

那一刻我真的想砸电脑。

后来我开始研究异步方案。Node.js?算了,团队都是Python栈。Go?虽然性能确实猛,但整个团队重构成本太高,而且……我承认,作为一个Mac党,我对Go的toolchain有点审美疲劳(别打我)。最终,FastAPI进入了我的视野。

异步不是噱头,是生存必需品

FastAPI最大的卖点是什么?很多人第一反应是自动生成Swagger文档,或者Pydantic的数据校验。但对我这种被性能折磨过的人来说,原生支持async/await才是王炸

看看这段代码:

from fastapi import FastAPI
import asyncio
import httpx

app = FastAPI()

@app.get("/users/{user_id}")
async def get_user(user_id: int):
    async with httpx.AsyncClient() as client:
        # 并发请求多个服务
        user_task = client.get(f"https://user-service/{user_id}")
        order_task = client.get(f"https://order-service/{user_id}")
        
        user_resp, order_resp = await asyncio.gather(user_task, order_task)
        
    return {
        "user": user_resp.json(),
        "orders": order_resp.json()
    }

短短十几行,就实现了并发调用下游服务。换成Flask的话,要么用线程池(GIL限制),要么用gevent打猴子补丁(黑魔法太多,线上出问题难排查)。

更骚的是,FastAPI底层用的Starlette框架,本身就是为高性能设计的。我在本地压测时,单核CPU下轻松跑到3000+ QPS,而同样逻辑的Flask应用只有800左右。这差距,简直是从自行车换到了特斯拉。

数据校验:少写50%的防御性代码

以前用Flask的时候,最烦的就是参数校验。前端传个{"age": "abc"}过来,你得手动判断类型、范围、必填项,写一堆if-else。测试同学每次提bug都说:“你们后端怎么不校验参数啊?”

FastAPI配合Pydantic,直接把这个问题干掉了:

from pydantic import BaseModel, Field
from typing import Optional

class UserCreate(BaseModel):
    username: str = Field(..., min_length=3, max_length=20)
    email: str
    age: int = Field(..., ge=18, le=120)  # 18-120岁
    is_active: Optional[bool] = True

@app.post("/users")
async def create_user(user: UserCreate):
    # 到这里,user已经确保是合法的了
    db.save(user.dict())
    return {"status": "success"}

自动校验、自动生成错误信息、还能在Swagger里看到字段约束。产品经理再也不用追着问“这个字段最大能填多少”了。

性能优化实战:从理论到生产

光说不练假把式。我把FastAPI用到生产环境后,做了几件关键的事:

1. 数据库连接池优化

很多新手会犯一个错误:在每个请求里创建新的数据库连接。这在高并发下简直是灾难。正确的做法是用全局连接池:

from databases import Database
from sqlalchemy import create_engine

DATABASE_URL = "postgresql://user:pass@localhost/db"

# 全局数据库实例
database = Database(DATABASE_URL)
engine = create_engine(DATABASE_URL)

@app.on_event("startup")
async def startup():
    await database.connect()

@app.on_event("shutdown")
async def shutdown():
    await database.disconnect()

@app.get("/items")
async def read_items():
    query = "SELECT * FROM items"
    return await database.fetch_all(query)

2. 缓存策略

对于读多写少的接口,Redis缓存是标配。FastAPI的依赖注入系统让缓存逻辑特别清晰:

from fastapi import Depends
import aioredis

async def get_redis():
    redis = aioredis.from_url("redis://localhost")
    try:
        yield redis
    finally:
        await redis.close()

@app.get("/expensive-data")
async def get_expensive_data(redis: aioredis.Redis = Depends(get_redis)):
    cache_key = "expensive_data_v1"
    cached = await redis.get(cache_key)
    if cached:
        return json.loads(cached)
    
    # 计算昂贵的数据
    data = compute_heavy_logic()
    await redis.setex(cache_key, 300, json.dumps(data))  # 缓存5分钟
    return data

3. 生产部署:别再用uvicorn --reload了!

开发时uvicorn main:app --reload确实爽,但生产环境必须用专业方案。我推荐Gunicorn + Uvicorn Worker:

# gunicorn.conf.py
workers = 4
worker_class = "uvicorn.workers.UvicornWorker"
bind = "0.0.0.0:8000"
timeout = 60
keepalive = 5

然后启动:

gunicorn -c gunicorn.conf.py main:app

这样既能利用多核CPU,又能享受Uvicorn的异步性能。

和Go比,FastAPI到底差在哪?

说到这儿,肯定有朋友要问:既然追求性能,为什么不直接上Go?毕竟Go的goroutine和channel模型在高并发场景下确实无敌。

我认真对比过。先看个简单的基准测试(4核CPU,16GB内存):

框架 QPS 内存占用 开发效率
Go (Gin) 28,000 12MB 中等
FastAPI 8,500 85MB 极高
Flask 1,200 75MB

Go的性能优势毋庸置疑,但Python的生态和开发速度也是实实在在的。特别是对于我们这种小团队创业,快速迭代比极致性能更重要。而且现在很多性能瓶颈其实在数据库和网络I/O,而不是语言本身。

另外,作为一个被Java折磨过、又被TypeScript劝退过的老程序员,我觉得工具应该服务于业务,而不是反过来。如果团队熟悉Python,硬要转Go,可能三个月都在踩坑,产品上线遥遥无期。

给新手的避坑指南

最后分享几个我踩过的坑,帮大家少走弯路:

  1. 不要滥用async:如果你的代码里没有I/O操作(比如纯CPU计算),用async反而会增加开销。同步代码就用普通def。

  2. ORM选择要谨慎:SQLAlchemy的async支持还在beta阶段,稳定性不如Tortoise ORM或Databases。我建议初期用Databases,简单直接。

  3. 中间件顺序很重要:CORS、认证、日志这些中间件的执行顺序会影响性能。把耗时的操作放后面。

  4. 别忽视类型提示:FastAPI重度依赖类型提示来生成文档和校验数据。不写类型提示等于白用。

  5. 测试!测试!测试!:FastAPI自带TestClient,写单元测试特别方便:

from fastapi.testclient import TestClient

def test_create_user():
    client = TestClient(app)
    response = client.post("/users", json={
        "username": "testuser",
        "email": "test@example.com",
        "age": 25
    })
    assert response.status_code == 200

写在最后:代码人生,不止一种活法

回头看这三年多的技术总监生涯,从天天救火到主动优化,从被动接受需求到推动架构升级,FastAPI算是给我职业生涯画上了一个漂亮的句号。

下周我就要去注册公司了,新项目全部用FastAPI+PostgreSQL+Redis的技术栈。虽然创业路上肯定还会遇到各种坑,但至少在技术选型上,我不再纠结。

对了,如果你也在考虑用FastAPI替代Flask,或者正在纠结要不要学Go,不妨先试试FastAPI。它可能不是最快的,但绝对是最适合Python开发者快速构建高性能API的选择。

毕竟,在这个卷成麻花的行业里,能让我们少加班、多陪家人、早点下班的技术,就是好技术。

代码人生,开心最重要。

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝