FastAPI真香!一个前端仔的后端初体验

代码自留地
2026-01-03 22:21
阅读 601

上周五晚上十点半,我盯着MacBook上那个卡成PPT的Node.js本地服务,突然意识到一件事:我这个天天研究Lottie动画、CSS Houdini的前端工程师,竟然要开始写后端了。

事情是这样的:公司最近在搞一个新产品原型,涉及区块链资产查询和交易记录展示。产品经理拍着胸脯说“就几个接口,很简单”,结果后端大哥被临时抽去支援另一个Java微服务项目,deadline还剩三天。领导看我平时GitHub上Python小脚本写得挺溜,直接甩过来一句:“你先顶上,用FastAPI搭个MVP。”

得,又到了程序员被迫全栈的时刻。

为什么选FastAPI?因为我懒啊

说实话,我本来想用Flask的——毕竟大学毕设就是用它糊的。但翻了翻文档发现FastAPI这玩意儿简直为我这种既要又要还要的人量身定制:

  • 自动生成Swagger文档:再也不用跟产品经理解释“接口还没写完所以文档没有”了
  • Pydantic数据校验:前端传参格式不对?直接422错误,省得我写一堆if判断
  • 异步支持:虽然这次用不上,但听说性能吊打Flask(后面测试确实如此)

最打动我的是它的类型提示——作为一个被TypeScript惯坏的前端,看到Python也能享受def get_user(user_id: int) -> User这种安全感,当场就决定试试。

从Hello World到区块链接口

先搞个最简示例压压惊:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def hello():
    return {"message": "前端仔的后端初体验"}

跑起来后访问/docs,自动出来的交互式文档让我惊呼“卧槽”——这比我们组Java Spring Boot项目里手写的YAPI文档好用十倍!

但现实很快打脸。当我尝试接入区块链节点查询交易时,遇到了第一个坑:同步阻塞

# 初版代码(别学!)
import requests

@app.get("/tx/{tx_hash}")
def get_transaction(tx_hash: str):
    # 这里会阻塞整个事件循环!
    resp = requests.get(f"https://eth-mainnet.g.alchemy.com/v2/xxx/{tx_hash}")
    return resp.json()

压力测试时QPS刚过50,Uvicorn进程CPU就飙到100%。想起之前面试某大厂时被问“如何提高Python服务并发”,当时支支吾吾答不上来,现在终于懂了——得用异步!

换成httpx后世界清净了:

import httpx

@app.get("/tx/{tx_hash}")
async def get_transaction(tx_hash: str):
    async with httpx.AsyncClient() as client:
        resp = await client.get(f"https://eth-mainnet.g.alchemy.com/v2/xxx/{tx_hash}")
    return resp.json()

QPS直接干到800+(本地测试数据),老板路过我工位时都多看了两眼屏幕。

数据库设计踩坑实录

产品需求里有个“用户资产历史记录”功能。我一开始偷懒用SQLite存本地,结果测试同学一导入10万条数据,查询慢得像在加载IE6。

赶紧切PostgreSQL,但建表时又犯了新手错误:

-- 错误示范:没加索引
CREATE TABLE asset_history (
    user_id VARCHAR(42),
    token_address VARCHAR(42),
    balance DECIMAL,
    timestamp TIMESTAMP
);

当用户查自己30天内的ETH变动时,EXPLAIN ANALYZE显示全表扫描,耗时1.2秒。运维同事路过冷笑:“你这接口上线怕不是要被DDoS?”

赶紧补复合索引:

CREATE INDEX idx_user_token_time 
ON asset_history (user_id, token_address, timestamp DESC);

查询时间降到23ms,这才有点生产环境的样子。

性能优化三板斧

在准备跳槽刷LeetCode间隙,我顺手对服务做了些优化:

  1. 连接池管理
    databases库替代原生psycopg2,避免每次请求新建DB连接:

    from databases import Database
    
    database = Database("postgresql://user:pwd@localhost/db")
    
  2. 缓存穿透防护
    区块链交易查询加Redis缓存,空结果也缓存5秒防刷:

    @app.get("/tx/{tx_hash}")
    async def get_transaction(tx_hash: str):
        cache_key = f"tx:{tx_hash}"
        if cached := await redis.get(cache_key):
            return json.loads(cached)
        
        # ...查询逻辑...
        await redis.setex(cache_key, 300, json.dumps(result))
        return result
    
  3. Gunicorn + Uvicorn组合拳
    本地开发用uvicorn main:app,生产环境上Gunicorn管理多worker:

    gunicorn -k uvicorn.workers.UvicornWorker --workers 4 main:app
    

最终压测结果(MacBook Pro M1):

配置 QPS 平均延迟
单worker同步 48 2083ms
单worker异步 812 123ms
4 worker异步 2940 34ms

这提升幅度,够我在周会上吹一周了。

和Java项目的对比感悟

我们主站是Spring Boot全家桶,每次改个接口都要等3分钟启动。而FastAPI改完代码保存即生效(配合--reload),调试效率高到飞起。

但不得不说,Java生态在分布式事务服务治理这些重型场景还是稳如老狗。FastAPI更适合做以下场景:

  • 快速验证产品想法(MVP阶段)
  • 中小型内部工具
  • 高I/O低计算的API网关

如果是搞区块链底层或者高频交易系统,该上Java还是得上——虽然我每次看到CompletableFuture.supplyAsync().thenApplyAsync()就想砸键盘。

给前端转后端的朋友几点建议

  1. 别迷信“简单”
    FastAPI虽然上手快,但生产环境要考虑日志监控、熔断降级、配置管理,这些和前端工程化一样复杂

  2. 数据库不是JSON存储
    我差点把所有区块链数据存成JSON字段,被DBA按在地上摩擦

  3. 异步不是银弹
    CPU密集型任务(比如解析大段ABI)还是要扔给Celery

最后说个搞笑的事:昨天产品问我“能不能加个WebSocket实时推送交易状态”,我正愁没机会用Starlette的WebSocket呢!结果他下一句是“不过下周可能改成轮询...”。果然,互联网没有银弹,只有不断变化的需求。

如果你也在用Mac写代码、边刷题边焦虑跳槽,不妨试试FastAPI——至少比调CSS居中简单多了(手动狗头)。

评论 0

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