FastAPI + Celery 异步任务队列实战:从环境搭建到进度监控的完整指南
摘要 :在使用 FastAPI 构建高性能 API 时,如何处理耗时任务而不阻塞主线程?本文通过一个完整的 Demo,从零搭建 FastAPI + Celery + Redis 异步任务队列,涵盖任务提交、状态追踪、进度更新及错误处理,助你掌握后端解耦的核心技术。
🎯 为什么你需要异步任务队列? 在构建现代 Web 应用时,我们常面临一个架构挑战:如何平衡接口的极速响应与后台的繁重计算?
如果将发送邮件、生成 PDF 报表或调用重型 AI 模型等操作放在 HTTP 请求的主线程中,FastAPI 的异步优势将荡然无存,用户不得不面对漫长的等待甚至超时。
解决方案是“解耦” :
FastAPI 负责优雅地接收指令,立即返回“任务已受理”。
Redis 作为消息中间件(Broker),充当缓冲区的神经中枢。
Celery 作为后台 Worker,在幕后无声地处理繁重逻辑。
本文将带你通过一个开箱即用 的 Demo,实现从任务调度到结果轮询的完整闭环。
📂 项目结构 我们将创建一个简洁明了的项目结构,便于理解和扩展:
1 2 3 4 5 6 fastapi_celery_demo/ ├── app.py # FastAPI 主应用(接口层) ├── celery_app.py # Celery 配置实例(核心配置) ├── tasks.py # 任务函数定义(业务逻辑) ├── docker-compose.yml # Redis 服务配置(基础设施) └── requirements.txt # Python 依赖
🛠️ 1. 环境准备 依赖安装 (requirements.txt) 1 2 3 4 fastapi==0.104.1 uvicorn[standard]==0.24.0 celery==5.3.4 redis==5.0.1
基础设施 (docker-compose.yml) 我们使用 Docker 快速启动一个 Redis 实例,作为 Celery 的 Broker 和 Backend。
1 2 3 4 5 6 7 8 version: "3.8" services: redis: image: redis:7-alpine ports: - "6379:6379" restart: unless-stopped
⚙️ 2. 核心代码实现 Celery 配置 (celery_app.py) 这是异步系统的“心脏”。我们配置了 Redis 连接,并开启了任务追踪和 UTC 时间支持。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from celery import Celeryapp = Celery( "fastapi_celery_demo" , broker="redis://localhost:6379/0" , backend="redis://localhost:6379/0" , ) app.conf.update( task_serializer="json" , accept_content=["json" ], result_serializer="json" , timezone="Asia/Shanghai" , enable_utc=True , task_track_started=True , task_time_limit=30 * 60 , task_soft_time_limit=25 * 60 , ) app.autodiscover_tasks(["tasks" ])
任务定义 (tasks.py) 这里定义了三种典型场景:简单计算、带进度的长任务、以及模拟失败的任务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import timefrom celery_app import app@app.task(name="tasks.add" , bind=True ) def add (self, x: int , y: int ) -> dict : """场景1:简单的加法任务""" self .update_state(state="PROCESSING" , meta={"progress" : "开始计算..." }) time.sleep(2 ) return {"task_id" : self .request.id , "x" : x, "y" : y, "result" : x + y} @app.task(name="tasks.sleep" , bind=True ) def sleep_task (self, seconds: int ) -> dict : """场景2:模拟长任务,支持实时进度更新""" for i in range (seconds): time.sleep(1 ) self .update_state( state="PROGRESS" , meta={ "current" : i + 1 , "total" : seconds, "progress" : f"{(i + 1 ) / seconds * 100 :.0 f} %" } ) return {"status" : "done" , "slept" : seconds} @app.task(name="tasks.fail" , bind=True ) def fail_task (self ) -> dict : """场景3:模拟任务失败""" time.sleep(1 ) raise ValueError("This task failed intentionally!" )
FastAPI 接口 (app.py) 这是系统的“门面”,负责接收请求并返回任务 ID,同时提供查询接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 from fastapi import FastAPI, HTTPExceptionfrom celery.result import AsyncResultfrom pydantic import BaseModelfrom celery_app import appfrom tasks import add, sleep_task, fail_taskfastapi_app = FastAPI(title="FastAPI + Celery Demo" ) class AddTaskRequest (BaseModel ): x: int y: int class SleepTaskRequest (BaseModel ): seconds: int @fastapi_app.post("/tasks/add" ) async def create_add_task (request: AddTaskRequest ): """提交加法任务""" task = add.delay(request.x, request.y) return {"task_id" : task.id , "status" : "submitted" } @fastapi_app.post("/tasks/sleep" ) async def create_sleep_task (request: SleepTaskRequest ): """提交长时任务""" if request.seconds <= 0 : raise HTTPException(status_code=400 , detail="seconds must be > 0" ) task = sleep_task.delay(request.seconds) return {"task_id" : task.id , "status" : "submitted" } @fastapi_app.get("/tasks/{task_id}" ) async def get_task_status (task_id: str ): """查询任务实时状态(含进度)""" task_result = AsyncResult(task_id, app=app) response = {"task_id" : task_id, "status" : task_result.state, "ready" : task_result.ready()} if task_result.state == "PROGRESS" : response["progress" ] = task_result.info elif task_result.ready(): if task_result.successful(): response["result" ] = task_result.result else : response["error" ] = str (task_result.info) return response
🚀 3. 启动与测试 第一步:启动 Redis
第二步:启动 Celery Worker 在新终端中运行:
1 celery -A celery_app worker --loglevel=info
看到 celery@your-host ready 即表示连接成功。
第三步:启动 FastAPI 再开一个新终端:
1 uvicorn app:fastapi_app --reload --host 0.0.0.0 --port 8000
第四步:交互式测试 访问 Swagger UI : http://localhost:8000/docs
POST /tasks/sleep : 输入 {"seconds": 5},获取 task_id。
GET /tasks/{task_id} : 多次刷新,观察 status 从 PENDING -> PROGRESS (带百分比) -> SUCCESS 的变化。
💡 进阶技巧
监控面板 :安装 flower (pip install flower) 并运行 celery -A celery_app flower,可以通过 Web 界面直观查看任务队列堆积情况。
并发控制 :启动 Worker 时增加 --concurrency=4 参数,根据 CPU 核心数调整并行处理能力。
生产环境建议 :在生产环境中,建议使用 Supervisor 或 systemd 管理 Celery Worker 进程,确保其崩溃后能自动重启。
🏁 结语 通过 FastAPI 与 Celery 的结合,我们成功将“即时响应”与“后台处理”分离。这种架构不仅提升了用户体验,也为系统未来的横向扩展打下了坚实基础。希望这个 Demo 能成为你构建高可用后端服务的有力起点。
喜欢这篇教程吗?欢迎在评论区分享你的异步任务实战经验!