从零开始的FastAPI之旅:写给新手后端开发者的实战笔记

写给机器的诗
2025-06-12 18:03
阅读 445

引言:为什么选择FastAPI?

引言:为什么选择FastAPI?

记得去年我刚从前端转岗到后端的时候,公司正在推进一个高并发数据服务项目的重构。我们原本用的是Django,虽然稳定、生态丰富,但随着接口数量和调用频率的增长,性能瓶颈渐渐显现出来——特别是在处理实时性要求高的API时,响应时间常常超出预期。

为了寻找一个更加轻量级、异步友好且原生支持类型提示的框架,我们尝试了几个Python的Web框架,最终选定了FastAPI。它不仅有极快的性能表现(官方说比Flask快很多),还有自动生成OpenAPI文档的能力,更重要的是,它对现代Python特性如async/await的支持非常出色。这让我们在构建高并发系统时有了更多的底气。

这篇文章将带你从零开始一步步入门FastAPI,结合我在真实项目中遇到的一些挑战与解决方案,帮助你少走弯路。


问题描述:我们在项目中遇到了什么痛点?

问题描述:我们在项目中遇到了什么痛点?

项目背景

我们正在做一个面向中小商家的数据分析平台,用户上传销售、库存等原始数据后,后台需要进行计算并返回各种可视化结果。业务增长迅速,原来的Django架构逐渐暴露出一些问题:

  • 接口响应慢,特别是查询历史数据时延迟显著
  • 文档维护困难,Swagger是手写的,经常不一致
  • 没法很好地利用异步特性提升吞吐量
  • 新人上手成本高,没有清晰的接口结构规范

这些都不是大问题,但如果放任不管,它们会像温水煮青蛙一样一点点拖垮系统质量。


解决方案:FastAPI能带给我们什么?

解决方案:FastAPI能带给我们什么?

我们决定用FastAPI来重构核心数据接口层。之所以选它,是因为它满足我们的几个关键需求:

  1. 高性能:基于Starlette和Pydantic,异步原生支持
  2. 自动文档生成:Swagger UI和Redoc开箱即用
  3. 类型安全:强类型的输入输出校验机制
  4. 模块化设计:容易拆分代码结构,便于团队协作

在实际使用过程中,我们确实感受到了这些优势带来的便利性,下面我会通过具体的技术实践,详细说明如何构建一个简洁、高效的FastAPI应用。


实战:FastAPI基础结构搭建

实战:FastAPI基础结构搭建

先从最简单的例子入手。安装FastAPI和Uvicorn:

pip install fastapi uvicorn

然后新建一个文件 main.py

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"message": "Hello, FastAPI!"}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

运行这个脚本后访问 http://localhost:8000/docs,就能看到自动生成的交互式API文档。

是不是很简单?但真正用于生产环境的服务远比这个复杂得多。下面是一个我们重构后的项目目录结构:

my_project/
├── app/
│   ├── api/
│   │   └── v1/
│   │       ├── endpoints/
│   │       │   ├── data.py
│   │       │   └── auth.py
│   │       └── __init__.py
│   ├── core/
│   │   └── config.py
│   ├── database/
│   │   ├── models.py
│   │   └── session.py
│   ├── schemas/
│   │   ├── data.py
│   │   └── user.py
│   ├── services/
│   │   ├── data_service.py
│   │   └── auth_service.py
│   └── main.py
├── alembic/
├── requirements.txt
└── README.md

这种结构清晰地划分了各部分职责,尤其适合中大型项目管理。


数据库集成:SQLAlchemy + AsyncSession

我们原来用的ORM是Django ORM,在迁移过程中发现它对异步支持有限,于是我们改用了SQLAlchemy搭配AsyncSession,配合asyncpg驱动PostgreSQL数据库。

举个例子,我们有一个data_records表用来存储每条销售记录。在models.py中定义如下:

from sqlalchemy import Column, Integer, String, DateTime, Numeric
from database.session import Base

class DataRecord(Base):
    __tablename__ = 'data_records'

    id = Column(Integer, primary_key=True)
    product_name = Column(String(100))
    amount = Column(Numeric(10, 2))
    created_at = Column(DateTime(timezone=True))

然后在异步函数中,我们这样操作数据:

from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select

async def get_data(db: AsyncSession, limit: int = 100):
    stmt = select(DataRecord).limit(limit)
    result = await db.execute(stmt)
    return result.scalars().all()

这里的关键是要引入AsyncSession而不是同步的sessionmaker。整个IO操作可以释放线程,让事件循环去处理其他请求,从而提高整体并发能力。

💡 小贴士:如果你使用的不是PostgreSQL而是MySQL,可以考虑用asyncmyaiomysql配合 SQLAlchemy 使用。


性能优化:异步+连接池

在处理大批量数据时,我们发现一个接口响应时间有时候能达到好几秒。分析下来发现主要是两个原因:

  1. 查询语句没加索引,全表扫描导致等待
  2. 同步调用阻塞主线程

为了解决这个问题,我们做了以下优化:

  • created_at字段加上索引,按时间筛选变得飞快
  • 所有数据库操作改为异步方式,并设置连接池大小
  • 对于批量插入任务,使用bulk_save_objects减少数据库交互次数

下面是连接池的配置示例:

from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker

engine = create_async_engine(
    settings.DATABASE_URL,
    pool_pre_ping=True,
    pool_size=5,
    max_overflow=10,
)

async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)

这样我们就可以在视图函数中注入一个异步的db对象:

@app.get("/data")
async def list_data(db: AsyncSession = Depends(get_db)):
    records = await get_data(db, limit=200)
    return {"records": records}

其中get_db依赖项负责创建并管理会话周期:

async def get_db():
    async with async_session() as session:
        yield session

踩坑经验:那些让人头秃的小细节

在实战中,我也踩了不少坑,分享几个印象深刻的:

1. Pydantic模型不能直接传SQLAlchemy对象

FastAPI底层使用Pydantic做数据验证和序列化,默认情况下不支持SQLAlchemy模型直接返回。比如:

return {"data": record}  # 这样不行

解决方法是在schema里定义好对应的Pydantic模型:

from pydantic import BaseModel
from datetime import datetime

class DataRecordSchema(BaseModel):
    id: int
    product_name: str
    amount: float
    created_at: datetime

    class Config:
        orm_mode = True  # 关键点!允许映射SQLAlchemy对象

这样就可以直接返回record对象了。


2. Uvicorn热重载在Windows下有问题

我们开发时喜欢用热加载功能:

uvicorn app.main:app --reload

但在Windows系统上会出现无限重启的问题。后来我们换成了Linux或者WSL2,问题就解决了。如果你坚持用Windows,也可以试下watchdog

pip install watchdog
uvicorn app.main:app --reload --reload-dir ./app

3. Docker部署时要注意多进程模式

我们在上线前测试的时候发现QPS始终上不去,排查了半天才发现Uvicorn默认只启了一个Worker。改成多进程之后,性能翻倍!

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80", "--workers", "4"]

建议worker数设为CPU核心数。如果跑在Kubernetes上,可以通过Helm Chart灵活控制并发数。


效果总结:重构带来的收益

项目重构完成后,我们进行了压测对比。以下是部分核心指标的变化:

指标 Django版本 FastAPI版本
单接口平均响应时间 350ms 80ms
支持同时在线用户数 ~500 ~2000
QPS(简单GET) 80 650
OpenAPI文档更新效率 手动维护 自动生成

除了性能提升外,团队的整体协作也更顺畅了。新同事只需要看API路径和Schema文档,就能快速理解接口逻辑,不再需要反复问老成员“这个地方是怎么做的”。


给新手的一些建议

作为一个从“前端切”过来的新人后端开发者,我想分享几点体会:

  1. 别急着追求完美结构:刚开始可以先把功能跑起来,不要被工程结构困住手脚。
  2. 多用类型提示:类型提示不仅能帮你预防错误,还能让你的IDE更好用。
  3. 文档比你想象的重要:尤其是对于多人协作项目,自动化文档真的能救命。
  4. 性能优化要早做预案:比如数据库索引、缓存策略这些,最好一开始就想清楚。
  5. 多学学Starlette的知识:FastAPI底层基于它构建了很多特性,了解它会让你更容易掌握高级玩法。

另外推荐两个学习资源:


结语:FastAPI不只是工具,更是工程习惯的体现

从最开始的一个简单Demo到现在承载千万级请求的服务,FastAPI陪伴我们走过了整个重构过程。它不仅是技术上的选择,更是一种现代化后端开发思维的体现。

希望这篇文章能帮助你少走些弯路。如果你也在学习FastAPI,欢迎留言一起交流心得。毕竟,后端这条路,走得越远越觉得“懂点东西”只是第一步,真正的考验,从来都是落地场景中的点滴细节。

共勉 🚀

评论 0

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