ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

FastAPI 性能扩展 Redis 缓存、Celery 异步任务与容器部署

FastAPI 性能扩展 Redis 缓存、Celery 异步任务与容器部署 任务详情接口被频繁刷新时数据库其实在重复回答同一个问题。把所有耗时工作都塞进 FastAPI 请求里也会出事用户点一次导出浏览器就一直转圈。这一篇给任务 API 加两条分流Redis 负责短暂记忆Celery 负责把耗时工作交给 worker。配套代码已经放在 fastapi-task-api文章中的完整实现以main分支为准。Redis 不是越早接越好缓存会带来失效问题所以项目只缓存一个最容易定义边界的对象任务详情。列表带分页和筛选条件缓存键会迅速膨胀先不碰它。defcache_key(owner_id:uuid.UUID,task_id:uuid.UUID)-str:returnftask:{owner_id}:{task_id}router.get(/{task_id},response_modelTaskRead)asyncdefread_task(task_id:uuid.UUID,sessionDepends(get_db_session),redisDepends(get_redis)):keycache_key(current_user.id,task_id)cachedawaitredis.get(key)ifcached:returnTaskRead.model_validate_json(cached)taskawaitowned_task(session,task_id,current_user.id)resultTaskRead.model_validate(task)awaitredis.set(key,result.model_dump_json(),ex300)# 缓存五分钟returnresult键里必须有用户 ID。即使任务 UUID 已经很难猜隔离条件也不能依赖运气。更新和删除任务后立刻删键下一次读取自动回源数据库。创建接口没有对应缓存因此不需要做任何操作。awaitsession.commit()awaitredis.delete(cache_key(current_user.id,task_id))缓存的正确性来自失效策略不来自缓存命中率。这就是为什么这里只放详情不拿 Redis 去替代数据库。BackgroundTasks 和 Celery 各干什么FastAPI 的BackgroundTasks适合短小、允许跟 Web 进程同生共死的事情例如记录一条审计日志。它不需要单独部署 worker。Celery 则把任务投给 broker由独立 worker 消费。导出 CSV、批量发邮件、调用慢速第三方接口都更适合这个路径。worker 可独立扩容也能配置重试。任务 API 用 Redis 同时充当 broker 和结果后端。场景选择写一条轻量审计日志BackgroundTasks导出任务 CSVCelery批量处理、失败重试Celery需要立即返回计算结果普通async接口把导出变成异步工作提交导出时接口立即返回202和task_id。客户端再用这个 ID 查询状态。为了避免任何登录用户猜到 ID 后读取结果项目把任务所有者短暂记录在 Redis 里。router.post(/exports/tasks,status_code202)asyncdefqueue_export(redisDepends(get_redis),current_userDepends(get_current_user)):resultexport_user_tasks.delay(str(current_user.id))awaitredis.set(fexport-owner:{result.id},str(current_user.id),ex3600)return{task_id:result.id,status:PENDING}worker 内部重新开数据库会话读取该用户的任务再生成 UTF-8 CSV 字符串。不要把 HTTP 请求里的 SQLAlchemy session 传给 Celery它无法跨进程序列化。celery_app.task(bindTrue,autoretry_for(Exception,),retry_backoffTrue,max_retries3)defexport_user_tasks(self,user_id:str)-str:returnasyncio.run(build_csv(uuid.UUID(user_id)))autoretry_for只适合可重试的临时失败例如数据库短暂不可用。CSV 格式错误或无效用户这类确定性错误重试三次也不会变好。大文件也不该塞进结果后端真实项目应该上传对象存储结果只返回下载地址。Celery WorkerRedisFastAPIClientCelery WorkerRedisFastAPIClientPOST 导出投递任务和保存所有者202 task_idworker 消费任务写入结果状态GET 导出状态PENDING 或 SUCCESS用 Docker Compose 把四个服务放到一起本地起 API 还不难真正容易遗漏的是 worker、PostgreSQL 和 Redis 的连接配置。docker-compose.yml定义四个服务PostgreSQL 和 Redis 有健康检查API 等数据库健康后先执行alembic upgrade head再启动。Copy-Item.env.example.env docker compose up--build.env中的DATABASE_URL主机名是postgresREDIS_URL主机名是redis它们来自 Compose 服务名。启动成功后打开/docs先注册、登录复制 Bearer 令牌再创建任务和提交导出。这一套配置不是高可用方案。生产环境还要接日志、指标、备份、密钥管理和外部对象存储。但 API、数据库、缓存和 worker 已经各自有了明确位置后续扩展不会全挤在 Web 进程里。本篇收口Redis 只缓存任务详情更新和删除时精确失效Celery 处理可延迟的 CSV 导出并返回可查询的任务 ID导出任务的所有者映射阻止其他用户读取结果Docker Compose 统一启动 API、PostgreSQL、Redis 与 worker
返回列表