用 Python 做后端 API,FastAPI 基本是现在的首选------原生异步支持、自动生成接口文档、开发效率高。但不少项目跑起来之后,并发一上来响应就变慢,很多时候不是框架不行,是架构和细节没处理好。本文从三个实战维度,结合可直接复用的代码,讲讲怎么把 FastAPI 服务的性能做扎实。
一、路由模块化拆分,依赖按需注入
很多新手写 FastAPI 喜欢把所有路由都堆在启动文件里,鉴权、日志、参数校验这些依赖全局全量挂载。项目小的时候看不出问题,接口一多,不仅代码臃肿难维护,每个请求都要走一遍无关的中间件逻辑,平白增加响应耗时。
优化思路很直接:按业务域拆分路由模块,依赖只在需要的路由层级注入,不要搞"全局一刀切"。
先按模块拆分路由文件,比如用户、订单各占一个文件,用 APIRouter 管理:
python
# app/routers/user.py
from fastapi import APIRouter, Depends
from ..dependencies.auth import verify_token
from ..services.user import get_user_by_id
# 定义用户模块路由,统一前缀和标签
router = APIRouter(prefix="/user", tags=["用户接口"])
# 仅当前模块的所有接口注入鉴权依赖,不影响其他路由
router.dependencies = [Depends(verify_token)]
@router.get("/{user_id}")
async def get_user_info(user_id: int):
user = await get_user_by_id(user_id)
return {"code": 0, "data": user}
主启动文件只做路由挂载和全局异常兜底,逻辑保持清爽:
python
# app/main.py
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from .routers import user, order
app = FastAPI(title="业务API服务")
# 按业务模块挂载路由,统一/api前缀
app.include_router(user.router, prefix="/api")
app.include_router(order.router, prefix="/api")
# 全局统一异常处理,避免业务异常直接抛出堆栈
@app.exception_handler(Exception)
async def global_error_handler(request: Request, exc: Exception):
# 生产环境替换为日志组件记录错误详情
print(f"请求异常 [{request.method} {request.url.path}]: {str(exc)}")
return JSONResponse(
status_code=500,
content={"code": 500, "msg": "服务内部错误"}
)
这样拆分之后,不仅代码可维护性变强,每个请求只会经过对应路由的依赖,减少了无效的逻辑执行。
二、异步数据库连接池 + 并行查询
Python API 的性能瓶颈,十有八九出在数据库 IO 上。FastAPI 的异步优势,必须搭配异步数据库驱动才能完全发挥;如果还用同步驱动、每次请求新建连接,再强的服务器也扛不住并发。
核心要做两件事:用连接池复用数据库连接,减少连接建立的开销;对无依赖的查询做并行执行,缩短总响应时间。
首先配置异步数据库连接池,这里以 MySQL + SQLAlchemy 异步版为例:
python
# app/db/database.py
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker
# 异步数据库连接串,使用 aiomysql 驱动
DATABASE_URL = "mysql+aiomysql://root:your_password@127.0.0.1:3306/biz_db"
# 创建异步引擎,配置连接池参数
engine = create_async_engine(
DATABASE_URL,
pool_size=20, # 常驻连接数,按服务器CPU核数调整
max_overflow=10, # 峰值时可额外创建的临时连接数
pool_recycle=3600, # 连接1小时后自动回收,避免超时断开
echo=False # 生产环境关闭SQL日志
)
# 异步会话工厂
AsyncSessionLocal = sessionmaker(
engine, class_=AsyncSession, expire_on_commit=False
)
# 依赖注入用的数据库会话生成器
async def get_db():
async with AsyncSessionLocal() as session:
yield session
业务层对无依赖的查询用 asyncio.gather 并行执行,总耗时等于最慢的那个查询,而不是串行累加:
python
# app/services/user.py
import asyncio
from sqlalchemy import text
from ..db.database import AsyncSessionLocal
async def get_user_detail(user_id: int):
async with AsyncSessionLocal() as db:
# 两个查询无依赖关系,并行执行
user_query = db.execute(
text("SELECT id, username, email FROM users WHERE id = :uid"),
{"uid": user_id}
)
order_query = db.execute(
text("SELECT id, amount FROM orders WHERE user_id = :uid LIMIT 10"),
{"uid": user_id}
)
user_res, order_res = await asyncio.gather(user_query, order_query)
return {
"user": user_res.mappings().first(),
"recent_orders": order_res.mappings().all()
}
三、热点数据缓存 + 响应压缩
对于读多写少的接口,缓存是提升 QPS 最见效的手段;再配合响应压缩,能进一步降低网络传输耗时,提升用户侧的响应速度。
单实例场景用内存缓存足够简单,多实例可以换成 Redis。这里实现一个轻量的内存缓存装饰器,搭配 FastAPI 自带的 GZip 压缩中间件。
python
# app/main.py
from fastapi import FastAPI
from starlette.middleware.gzip import GZipMiddleware
import time
from functools import wraps
app = FastAPI()
# 全局启用GZip压缩,大于1KB的响应自动压缩
app.add_middleware(GZipMiddleware, minimum_size=1024)
# 简易内存缓存装饰器,支持设置过期时间
def memory_cache(expire_seconds: int = 60):
cache_store = {}
def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
# 以函数参数生成缓存key,实际项目可结合请求路径优化
cache_key = f"{func.__name__}:{str(args)}:{str(sorted(kwargs.items()))}"
# 命中缓存且未过期直接返回
if cache_key in cache_store:
data, expire_time = cache_store[cache_key]
if time.time() < expire_time:
return data
# 未命中则执行原函数并写入缓存
result = await func(*args, **kwargs)
cache_store[cache_key] = (result, time.time() + expire_seconds)
return result
return wrapper
return decorator
# 模拟数据库查询
@memory_cache(expire_seconds=30)
async def query_hot_articles():
# 实际业务中此处为数据库查询
return [{"id": i, "title": f"热点文章{i}"} for i in range(10)]
# 热点列表接口,加30秒缓存
@app.get("/api/hot-articles")
async def hot_articles():
data = await query_hot_articles()
return {"code": 0, "data": data}
对于不常变更的静态数据,还可以在响应头里加 Cache-Control,让客户端和CDN直接缓存,进一步减轻服务器压力。
最后
FastAPI 的性能优化不用搞太多花里胡哨的操作,把路由拆干净、数据库连接复用好、热点数据加上缓存,绝大多数业务场景的性能都足够用。优化前建议先拿压测工具跑一遍,定位真实瓶颈再动手,避免上来就全量改造,徒增维护成本。