在现代Web应用开发中,我们经常会遇到一些耗时操作,比如发送邮件、处理图片、生成报表等。如果将这些操作放在HTTP请求响应周期内同步执行,会导致用户体验极差 - 用户需要等待很长时间才能看到页面响应。更糟糕的是,如果请求超时,这些操作可能会被中断,导致数据不一致。
这就是我们需要引入异步任务队列系统的原因。通过将耗时操作放入队列,由后台工作进程异步执行,可以立即响应用户请求,提升系统吞吐量和用户体验。Celery作为Python生态中最成熟的任务队列解决方案,配合RabbitMQ这一高性能消息代理,能够很好地解决这类问题。
Celery的架构主要包含三个部分:
这种生产者-消费者模式使得系统可以水平扩展 - 我们可以根据需要增加worker数量来处理更多任务。
RabbitMQ在Celery架构中扮演消息代理(Broker)的角色,负责接收、存储和分发任务消息。相比其他消息队列如Redis,RabbitMQ提供了更丰富的特性:
首先需要安装Celery和RabbitMQ的Python客户端:
bash复制pip install celery pika
RabbitMQ服务可以通过Docker快速启动:
bash复制docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:management
创建一个基础的Celery应用只需要几行代码:
python复制from celery import Celery
app = Celery(
'tasks',
broker='amqp://guest:guest@localhost:5672//',
backend='rpc://'
)
这里我们配置了:
通过@app.task装饰器可以轻松定义异步任务:
python复制@app.task
def add(x, y):
import time
time.sleep(5) # 模拟耗时操作
return x + y
这个简单的加法函数模拟了一个需要5秒执行的耗时操作。
在Web应用中调用这个任务非常简单:
python复制result = add.delay(4, 4)
print(result.id) # 获取任务ID
delay()方法是apply_async()的快捷方式,它会立即返回一个AsyncResult对象,而不会阻塞当前进程。
我们可以通过任务ID查询执行状态:
python复制from celery.result import AsyncResult
res = AsyncResult(result.id, app=app)
print(res.state) # PENDING|STARTED|SUCCESS|FAILURE
print(res.get(timeout=1)) # 阻塞获取结果
在生产环境中,建议使用更可靠的结果存储:
python复制app.conf.result_backend = 'redis://localhost:6379/0'
Redis比RPC更适合作为结果后端,因为它支持持久化和更丰富的查询功能。
在config.py中集中管理配置:
python复制app.conf.update(
task_serializer='json',
accept_content=['json'],
result_serializer='json',
timezone='Asia/Shanghai',
enable_utc=True,
)
启动worker时建议指定并发数:
bash复制celery -A tasks worker --loglevel=info --concurrency=4
对于CPU密集型任务,并发数建议设置为CPU核心数;对于IO密集型,可以适当提高。
可能原因:
解决方案:
bash复制# 检查RabbitMQ状态
docker ps | grep rabbitmq
# 检查端口监听
netstat -tulnp | grep 5672
可能原因:
解决方案:
python复制# 确保配置了正确的结果后端
app.conf.result_backend = 'redis://localhost:6379/0'
# 设置更长的结果过期时间
app.conf.result_expires = 3600 # 1小时
Celery beat可以处理周期性任务:
python复制app.conf.beat_schedule = {
'add-every-30-seconds': {
'task': 'tasks.add',
'schedule': 30.0,
'args': (16, 16)
},
}
启动beat服务:
bash复制celery -A tasks beat --loglevel=info
可以配置不同的队列处理不同类型的任务:
python复制app.conf.task_routes = {
'tasks.add': {'queue': 'high_priority'},
'tasks.mul': {'queue': 'low_priority'},
}
启动worker时指定队列:
bash复制celery -A tasks worker -Q high_priority,low_priority
Flower是Celery的实时监控工具:
bash复制pip install flower
celery -A tasks flower --port=5555
通过http://localhost:5555可以查看:
当单个worker无法满足需求时,可以:
启动多个worker示例:
bash复制# 服务器1
celery -A tasks worker -Q high_priority --hostname=worker1@%h
# 服务器2
celery -A tasks worker -Q low_priority --hostname=worker2@%h
在电商项目中,我们使用Celery+RabbitMQ处理以下场景:
关键配置经验:
一个典型的订单处理任务:
python复制@app.task(bind=True, max_retries=3)
def process_order(self, order_id):
try:
order = Order.objects.get(id=order_id)
# 扣减库存
reduce_inventory(order)
# 发送确认邮件
send_confirmation_email(order)
except Exception as exc:
self.retry(exc=exc, countdown=60)
这种配置可以在任务失败时自动重试3次,每次间隔60秒。