从零开始用 FastAPI 搞定后端开发:一次真实项目实践的全记录

PixelPerfect
2025-06-22 22:24
阅读 520

开篇:为什么我选择了 FastAPI?

开篇:为什么我选择了 FastAPI?

我是小林,目前在一家中小型互联网公司负责后端服务的开发与维护。公司主推的是一个面向中小企业的数据服务平台,用户量不算特别大,但对实时性和接口稳定性要求却非常高。

去年我们启动了一个新模块——客户行为分析系统。这个模块要做的事情是接收前端埋点上报的数据,清洗处理后入库,并对外提供数据查询、报表生成等能力。

当时我们团队内部讨论技术选型,考虑继续沿用 Django 或者尝试下 Go,甚至还有人提过 Flask。最后,我提出能不能试试 FastAPI,理由很简单:Python 语法简洁、异步支持好、文档自动化强、类型提示完善,而且在实际测试中性能表现也不错。

虽然最初团队对 FastAPI 的接受度不高,担心生态和上手难度,但我们还是决定以这个项目作为试点。没想到这一试,从此“一发不可收拾”。

今天这篇分享,就围绕这个项目的实战经验展开,带你一步步从入门到落地使用 FastAPI,尤其是面对一些典型问题时的思考路径。


项目背景 & 遇到的问题

项目背景 & 遇到的问题

我们的目标是实现一个高并发、低延迟的行为数据收集 API 接口。这个系统需要:

  • 实时接收客户端埋点事件(POST)
  • 对数据进行清洗校验
  • 存储到 ClickHouse 中
  • 提供简单的数据统计接口

初期我们计划使用 Django REST Framework,但很快就发现了几个关键问题:

  1. 同步模型难以应对高并发写入

    • 埋点上报的流量存在波峰波谷,Django 默认的 WSGI 同步模式扛不住突发请求。
  2. 缺乏自动化的 OpenAPI 文档体系

    • 我们的前后端协作较多,没有好的文档体系,导致大量沟通成本。
  3. 类型检查不够规范,容易引发运行时错误

    • 数据结构复杂、字段繁多,手工定义 Schema 很容易出错。

这些痛点促使我们转向了 FastAPI。它天然支持 async/await 异步模式,内置了 Pydantic 数据模型做类型验证,OpenAPI 自动化文档开箱即用,再加上 Python 的易读性,让我们觉得值得一试。


技术方案与实现思路

技术方案与实现思路

1. 整体架构设计

我们采用了如下架构图所示的简单结构:

[Client SDK] --> [Nginx (负载均衡)] 
               |
               +--> [FastAPI Service]
                            |
        +--------------------+---------------------+
        |                                          |
[Redis缓存]                                 [ClickHouse DB]

FastAPI 负责核心逻辑处理,包括数据校验、分发、存储。整个流程大致如下:

  1. Client 发送 POST 请求,上传 JSON 数据;
  2. FastAPI 接收到请求后进行基础鉴权;
  3. 使用 Pydantic Model 校验参数合法性;
  4. 清洗数据后,异步写入 Redis 缓冲队列;
  5. 另一个独立消费者服务定时拉取并入库 ClickHouse;
  6. 查询接口走 FastAPI + SQLAlchemy ORM 查询展示。

2. 路由与接口设计

为了快速构建,我们使用了 FastAPI 内置的路由装饰器机制。举个例子:

from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from typing import List

router = APIRouter(prefix="/v1/events")

class EventModel(BaseModel):
    event_name: str
    user_id: str
    timestamp: int
    properties: dict

@router.post("/")
async def receive_event(event: EventModel):
    # 核心业务逻辑
    await redis_queue_push(event.json())
    return {"status": "ok"}

这段代码干了这么几件事:

  • 定义一个 /v1/events 路由组
  • 设计 EventModel 用于数据结构校验(Pydantic 大法好)
  • 实现 POST 接口,直接对接 JSON Body 解析 + 类型校验
  • 返回标准格式响应

最爽的一点就是,只要定义好 BaseModel,Swagger UI 自动生成 API 文档,再也不用手动写 Markdown 接口文档了!


3. 异步写入的优化设计

为了提高写入吞吐,我们在接收到请求之后并不是直接落盘,而是先写入 Redis 的 List 结构,然后后台通过消费者协程消费写入 ClickHouse。

这样做的好处:

  • 提高响应速度(减少等待时间)
  • 实现生产-消费者模式,降低数据库压力
  • 更容易做失败重试

写入示例代码:

import aioredis

redis = await aioredis.create_redis_pool("redis://localhost")

async def redis_queue_push(data: str):
    await redis.rpush("event_queue", data)

而消费者则是单独部署的一个常驻进程,每隔一秒轮询是否有待处理数据:

while true; do
  python consumer.py
  sleep 1
done

当然更推荐配合 Celery 或 RQ 等任务队列来调度。


4. 数据库接入方式

对于查询类接口,我们采用了 SQLAlchemy ORM 进行封装,虽然官方不推荐 ORM 配合 async 使用,但在实际中结合 sqlalchemy.ext.asyncio 模块效果还不错。

比如我们定义了一个简单的 DAO 层:

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from models import EventRecord

async def get_daily_count(db: AsyncSession):
    stmt = select(func.count()).where(EventRecord.timestamp > today())
    result = await db.execute(stmt)
    return result.scalar_one()

这样的设计让代码具备较好的可维护性,同时又保留了 SQL 操作的灵活性。


5. 性能瓶颈排查经历

上线初期遇到了一个意想不到的问题:单节点 QPS 到不了预期值,高峰期出现连接排队现象。

我们通过日志、Grafana 监控以及压测发现:

  • FastAPI 单进程默认使用 Uvicorn 的 sync worker
  • 由于我们误用了同步的 Redis 客户端,阻塞了整个 event loop,导致并发下降

解决方法也很简单:

  • 换用 aioredis 替代原生 redis-py
  • 部署方式改为 Gunicorn + Uvicorn workers(多 worker 模式)

配置如下:

gunicorn main:app --workers=4 --worker-class uvicorn.workers.UvicornWorker --bind=0.0.0.0:8000

调整后性能提升明显,QPS 提升近 3 倍,RT 平均从 80ms 降到 25ms 左右。


成果与收益总结

成果与收益总结

经过两个多月的迭代开发和线上验证,我们成功将这套 FastAPI 构建的服务部署到了生产环境,并取得了以下成果:

维度 效果对比
启动效率 新人两天即可掌握框架基本用法
文档一致性 接口文档与实现保持完全一致
异步性能优势 高并发场景下比 Django 快约 30%-50%
类型安全保障 开发过程中因类型错误引起的 bug 减少 70%

此外,由于 FastAPI 的社区生态也在快速发展,我们还顺带引入了像 fastapi-users 这样的开源组件,快速实现了用户认证系统。


我的经验与建议

数据库设计模型-1

如果你是一个刚接触 FastAPI 的开发者,或者打算用它来做项目,以下几点是我亲身踩坑后的忠告:

✅ 1. 不要忽视类型安全的重要性

FastAPI 最大的优势之一是 基于 Pydantic 的类型安全机制。一定要善用它,尤其是在参数校验、返回值格式等方面。这不仅帮你减少 Bug,也极大提升了代码的可读性和维护性。

小贴士:如果不确定某个字段是否必填,可以用 Optional 来标注,避免频繁空指针异常。


✅ 2. 认真对待异步函数的调用链

Async 是一把双刃剑。一旦你在一个 view 里用了 async def,那么你调用的所有 I/O 操作(如数据库、缓存)都要是 async 的。否则就会阻塞 event loop,性能反而更差。

所以在项目初期就要统一异步调用链,避免后期重构。


✅ 3. 压测必须提前做

FastAPI 本身性能很好,但最终的表现很大程度取决于你的代码结构。建议上线前务必进行基准测试,比如用 Locust、wrk 等工具做压力测试。

我在测试中就发现了一个诡异的问题:当返回值包含大量浮点数时,JSON 序列化性能会明显下降,后来换成了 orjson 解决。


✅ 4. 日志、监控与报警不可或缺

FastAPI 本身不强制绑定任何日志方案,但强烈建议你尽早集成:

  • Structured logging(例如使用 loguru / structlog)
  • Prometheus metrics(可以接入 fastapi.middleware.PrometheusMiddleware)
  • Sentry 错误追踪

这些手段帮助我们及时定位了很多线上异常情况。


写在最后:FastAPI 适合谁?

FastAPI 绝不是银弹,但它非常适合:

  • 想要兼顾性能和开发效率的中小团队
  • 需要高频更新 API 的业务场景
  • Python 栈偏爱者
  • 重视自动化文档和类型安全的工程团队

如果你正在寻找一个现代、灵活且高性能的 Python 后端框架,我会毫不犹豫地推荐 FastAPI。

我也从最初的怀疑派变成了忠实粉丝,甚至最近还在推动公司内部统一后端语言为 Python + FastAPI 的组合。

希望这篇文章能帮你节省几天甚至几周的探索时间,愿你在开发路上少走弯路,写出更多高质量的代码 🚀


附录资源推荐

欢迎留言交流你的项目经验或 FastAPI 使用心得!

评论 0

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