目录
一、使用celery
celery需要中间件来做消息传递和结果存储,常用的消息中间件为:RabbitMQ、RocketMQ、Redis,其中redis最为方便,但没有web管理页面。
python
pip install celery
pip install redis # 使用 Redis 作为 broker/backend
二、通用celery项目目录结构
project/ # 项目root根路径 ├── celery_app/ # celery根目录 │ ├── tasks/ # celery taks异步任务分模块 │ │ ├── module1/ │ │ │ ├── __init__.py │ │ │ └── tasks.py │ │ ├── module3/ │ │ │ ├── __init__.py │ │ │ └── tasks.py │ │ ├── module3/ │ │ │ ├── __init__.py │ │ │ └── tasks.py │ │ ├── __init__.py │ ├── __init__.py # celery启动文件(也可以写一个其他的py文件作为入口) │ └── config.py # celery配置文件
三、相关文件代码
3.1、celery启动文件
python
# __init__.py
from celery import Celery
from celery_app.config import broker_url, result_backend
# 创建 Celery 实例
app = Celery(
'myapp',
broker=broker_url if broker_url else 'redis://localhost:6379/0', # 消息代理
backend=result_backend if result_backend else 'redis://localhost:6379/1', # 结果后端
)
# 从配置文件加载配置(推荐)
app.config_from_object('config')
# 自动发现任务模块
app.autodiscover_tasks(['tasks'])
3.2、celery配置文件
python
# config.py
from celery.schedules import crontab
# Broker 配置(使用redis的两个库作为celery的broker和backend)
broker_url = 'redis://localhost:6379/0'
result_backend = 'redis://localhost:6379/1'
# 序列化配置
task_serializer = 'json'
result_serializer = 'json'
accept_content = ['json']
timezone = 'Asia/Shanghai'
enable_utc = True
# 任务配置
task_track_started = True # 跟踪任务开始状态
task_time_limit = 30 * 60 # 任务执行超时时间(秒)
broker_connection_retry_on_startup = True # broker连接失败自动重试
# 结果配置
result_expires = 3600 # 结果过期时间(秒)
result_extended = True # 存储更多结果信息
# 重试配置
task_acks_late = True # 延迟确认任务
task_reject_on_worker_lost = True # Worker 丢失时拒绝任务
# 并发配置(启动时也可指定)
worker_concurrency = 4 # Worker 并发数
# celery beat定时调度配置(需要启动celery_beat)
beat_schedule = {
"test_beat_schedule": {
"task": "celery_app.tasks.module1.tasks.celery_beat_schedule",
"schedule": crontab(hour='10,11', minute=30),
}
}
3.3、异步任务定义
python
# module1.task.py
import requests
# 带重试配置的任务
@app.task(max_retries=5, default_retry_delay=60)
def request_api(url):
try:
response = requests.get(url, timeout=10)
return response.json()
except Exception as e:
raise request_api.retry(exc=e)
# 普通异步任务
@app.task
def async_task():
pass
# beat定时调度任务
@app.task()
def celery_beat_schedule():
pass
3.4、异步任务调用
任务调用方面,celery提供了很多种调用方式(delay、apply_async、signature),用来处理简单的异步任务或任务链任务(B依赖A的结果这种),普遍的单个异步任务使用apply_async方法足够了。
python
from celery_app import app
import time
# 执行异步任务获取结果
@app.task
def add(x, y):
print(f"add 开始执行,线程: {time.strftime('%H:%M:%S')}")
time.sleep(3) # 模拟耗时操作
result = x + y
print(f"add 完成: {result}")
return result
@app.task
def callback(result):
print(f"callback 开始执行,线程: {time.strftime('%H:%M:%S')}")
time.sleep(2)
print(f"callback 完成,处理结果: {result}")
return f"processed_{result}"
@app.task
def callback():
print('callback回调函数执行出错时调用,若异常被捕获则不会走到这里')
if __name__ == '__main__':
# 提交任务(带回调)
async_result = add.apply_async(
args=[4, 6],
link=callback.s(),
link_error=callback_error.s()
)
async_task_id = async_result.id
print(f"任务已提交,任务ID: {async_task_id }")
print(f"主线程继续执行: {time.strftime('%H:%M:%S')}")
# 使用 get()(阻塞主线程)
result = add.delay(4, 6)
value = result.get() # ❌ 阻塞,等待 add 完成
print("等待后才执行") # 需要等待
四、celery启动命令
python
# celery启动worker 并发数 队列 日志级别 后台运行
celery -A celery_app worker --concurrency=4 --queues=high_priority,default --loglevel=info --detach --logfile=/var/log/celery.log
# celery beat调度启动
celery -A celery_app beat --loglevel=info
# 安全的停止celery
celery -A celery_app control shutdown
# 查看活跃任务
celery -A celery_app inspect active
# 查看注册的任务
celery -A celery_app inspect registered
# 查看 Worker 统计信息
celery -A celery_app inspect stats
# 查看队列长度
celery -A celery_app inspect active_queues
# 撤销任务
celery -A celery_app control revoke <task_id>
# 终止任务(强制)
celery -A celery_app control revoke <task_id> --terminate
五、其他
python
# 常用命令
celery -A app worker -l info # 启动 Worker
celery -A app beat -l info # 启动 Beat
celery -A app flower # 启动监控
celery -A app purge # 清空所有消息
celery -A app inspect active # 查看活跃任务
celery -A app control revoke <id> # 撤销任务
# 常用装饰器
@app.task # 基础任务
@app.task(bind=True) # 绑定任务
@app.task(max_retries=3) # 带重试
@app.task(rate_limit='10/m') # 限流(每分钟10次)
@app.task(time_limit=30) # 超时控制
# 常用调用
task.delay(*args, **kwargs) # 简单调用
task.apply_async(args, countdown=10) # 延迟调用
task.s(*args, **kwargs) # 签名