1. 什么是 Celery?
Celery 是一个基于消息队列的分布式任务队列框架,主要用于处理异步任务、定时任务和后台任务。
简单来说:
Celery 可以把耗时操作从 Web 请求流程中剥离出来,让接口快速返回,然后在后台慢慢执行任务。
例如:
用户请求:
提交订单
上传文件
调用大模型分析
发送邮件
生成报表
这些操作可能需要几秒甚至几分钟。
如果直接在接口中执行:
请求
|
|
执行任务(30秒)
|
|
返回结果
用户需要等待30秒。
使用 Celery:
请求
|
|
创建任务
|
|
立即返回task_id
|
|
后台Worker执行
|
|
保存结果
用户体验会更好。
2. 为什么需要 Celery?
在普通 Web 服务中:
客户端
|
|
API服务
|
|
执行耗时任务
|
|
返回结果
存在几个问题:
2.1 请求阻塞
例如调用LLM:
用户请求
|
|
调用LLM
|
|
等待30秒
|
|
返回
接口连接一直占用。
2.2 服务重启导致任务丢失
例如:
任务开始执行
服务发布
服务重启
任务消失
2.3 无法控制任务数量
例如:
同时来了1000个任务:
任务1
任务2
任务3
...
任务1000
全部执行可能导致:
-
数据库压力过大
-
第三方接口限流
-
服务崩溃
Celery可以解决这些问题。
3. Celery核心组成
Celery主要包含几个角色:
3.1 Producer(生产者)
负责创建任务。
通常就是:
-
Web接口
-
定时任务
-
其他服务
例如:
用户请求接口
|
|
创建Celery任务
3.2 Broker(消息中间件)
负责存放任务消息。
常见:
-
Redis
-
RabbitMQ
流程:
Producer
|
|
↓
Redis
|
|
↓
Worker
Redis相当于任务的中转站。
3.3 Worker(消费者)
真正执行任务的进程。
例如:
Celery Worker
收到任务
执行代码
返回结果
3.4 Backend(结果存储)
用于保存任务执行结果。
可以使用:
-
Redis
-
MySQL
-
PostgreSQL
不过在业务系统中,通常推荐自己维护任务表。
4. Celery工作流程
完整流程:
客户端
|
|
调用接口
|
|
创建任务
|
|
Producer
|
|
Broker(Redis)
|
|
Worker
|
|
执行任务
|
|
保存结果
例如:
用户请求:
POST /analyze
{
"session_id":"abc"
}
接口:
创建task_id
发送Celery任务
返回task_id
后台:
Worker收到任务
获取聊天记录
调用LLM
保存数据库
更新状态
5. 安装Celery
安装:
pip install celery redis
6. 创建第一个Celery任务
项目结构:
project
├── celery_app.py
├── tasks.py
└── main.py
celery_app.py
from celery import Celery
app = Celery(
"demo",
broker="redis://localhost:6379/0",
backend="redis://localhost:6379/1"
)
这里:
broker:
表示任务发送到哪里。
backend:
表示任务结果保存在哪里。
tasks.py
from celery_app import app
@app.task
def add(x, y):
return x + y
@app.task
表示:
把普通Python函数变成Celery任务。
7. 启动Worker
执行:
celery -A tasks worker --loglevel=info
启动后:
Worker ready
表示等待任务。
8. 调用任务
普通调用:
add(1,2)
这是同步执行。
Celery调用:
add.delay(1,2)
执行:
代码发送任务
↓
Redis
↓
Worker执行
返回:
AsyncResult
里面包含:
task_id
9. AsyncResult是什么?
例如:
result = add.delay(1,2)
返回:
task_id:
a123456
可以查询:
result.get()
获取:
3
10. 在Web项目中的典型使用
例如:
用户上传文件:
POST /upload
接口:
def upload():
task = process_file.delay(file_id)
return {
"task_id":task.id
}
后台:
@app.task
def process_file(file_id):
process(file_id)
save_result()
用户:
GET /result/task_id
查询数据库:
处理中
完成
失败
11. Celery和线程池、Semaphore的区别
很多开发者容易混淆。
Semaphore
解决:
同时允许多少个任务执行
例如:
限制LLM:
Semaphore(3)
表示:
最多3个请求同时调用LLM。
Celery
解决:
任务如何可靠执行
包括:
-
排队
-
消费
-
重试
-
异步
-
扩容
二者不是替代关系。
实际生产:
Celery Worker
|
Semaphore
|
第三方接口
12. LLM场景中的Celery设计
例如:
业务:
用户提交session_id
↓
获取聊天记录
↓
调用LLM
↓
保存答案
推荐:
API服务
|
|
Celery
|
|
Worker
|
|
获取聊天服务
|
|
LLM
|
|
MySQL
任务状态:
PENDING
RUNNING
SUCCESS
FAILED
13. Celery常见问题
13.1 任务执行失败怎么办?
使用retry:
@app.task(
bind=True,
max_retries=3
)
def task(self):
try:
do_work()
except Exception as e:
raise self.retry(
exc=e,
countdown=30
)
13.2 任务重复执行怎么办?
Celery无法保证任务绝对只执行一次。
所以业务代码需要幂等。
例如:
保存结果前检查:
task_id是否已经完成
13.3 Worker数量如何设置?
例如:
celery worker -c 4
表示:
同时执行4个任务。
14. 什么时候应该使用Celery?
适合:
✅ 邮件发送
✅ 文件处理
✅ 视频转码
✅ 报表生成
✅ AI任务
✅ 大模型调用
✅ 数据同步
不适合:
❌ 简单计算
❌ 几毫秒完成的接口
❌ 强实时交易流程
15. 总结
Celery解决的问题:
如何让耗时任务可靠地在后台执行。
核心思想:
接口负责接收任务
Celery负责执行任务
数据库负责记录状态
用户通过task_id查询结果
在实际项目中:
FastAPI
+
Celery
+
Redis
+
MySQL
是一种非常常见的异步任务架构。
理解 Celery 最重要的一句话:
不要让用户请求等待耗时任务,把任务放入队列,让后台Worker慢慢处理。