Producer : 任务生产者. 调用 Celery API , 函数或者装饰器, 而产生任务并交给任务队列处理的都是任务生产者。
Broker : 消息代理, 队列本身. 也称为消息中间件. 接受任务生产者发送过来的任务消息, 存进队列再按序分发给任务消费方(通常是消息队列或者数据库). 通常用 RabbitMQ或者Redis
Celery Beat : 任务调度器. Beat 进程会读取配置文件的内容, 周期性的将配置中到期需要执行的任务发送给任务队列.例如如下配置的一个周期性执行的一个任务:
CELERYBEAT_SCHEDULE = {
'send_mail': {
'task': 'work.notify.email.send_mail',
# 'schedule': timedelta(minute=1),
'schedule': crontab(minute='*/1'),
'args': ('usr', 'sub', 'msg')
}
}
任务调度主要是为了解决业务场景中定时或周期任务,分别使用timedelta和crontab来定义计划任务,crontab的精度无法精确到秒时可使用timedelta代替,CELERYBEAT_SCHEDULE下可以定义多个计划/周期任务,send_mail为任务名称,task为任务单元导入名,schedule为具体调度,args为任务单元的参数.
运行时可先启动work进程池(celery worker -A work.app -l info)然后再启动beat进程池(celery beat -A work.app -l info),观察会发现beat进程每分钟生成一个任务,work进程发现任务后立即执行
Celery Worker : 执行任务的消费者, 通常会在多台服务器运行多个消费者, 提高运行效率.
Result Backend : 任务处理完成之后保存状态信息和结果, 以供查询.
使用于生产环境的消息代理有 RabbitMQ 和 Redis, 官方推荐 RabbitMQ.
方案 | 说明 |
---|---|
pickle | pickle 是Python 标准库中的一个模块, 支持 Pyuthon 内置的数据结构, 但他是 Python 的专有协议. Celery 官方不推荐. |
son | json 支持多种语言, 可用于跨语言方案. |
yaml | yaml 表达能力更强, 支持的数据类型较 json 多, 但是 python 客户端的性能不如 json |
msgpack | 二进制的类 json 序列化方案, 但比 json 的数据结构更小, 更快. |
配置项 | 说明 |
---|---|
CELERY_DEFAULT_QUEUE | 默认队列 |
CELERY_BROKER_URL | Broker 地址 |
CELERY_RESULT_BACKEND | 结果存储地址 |
CELERY_TASK_SERIALIZER | 任务序列化方式 |
CELERY_RESULT_SERIALIZER | 任务执行结果序列化方式 |
CELERY_TASK_RESULT_EXPIRES | 任务过期时间 |
CELERY_ACCEPT_CONTENT | 指定任务接受的内容类型(序列化) |
代码示例:
# 安装
$ pip install celery, redis, msgpack
# 配置文件 celeryconfig.py
CELERY_BROKER_URL = 'redis://localhost:6379/1'
CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TASK_RESULT_EXPIRES = 60 * 60 * 24 # 任务过期时间
CELERY_ACCEPT_CONTENT = ["json"] # 指定任务接受的内容类型.
# 初始化文件 celery.py
from __future__ import absolute_import
from celery import Celery
app = Celery('proj', include=["proj.tasks"])
app.config_from_object("proj.celeryconfig")
if __name__ == "__main__":
app.start()
# 任务文件 tasks.py
from __future__ import absolute_import
from proj.celery import app
@app.task
def add(x, y):
return x + y
# 启动消费者
$ celery -A proj worker -l info
# 在终端中测试
> from proj.tasks import add
> r = add.delay(2,4)
> r.result
6
> r.status
u"SUCCESS"
> r.successful()
True
> r.ready() # 返回布尔值, 任务执行完成, 返回 True, 否则返回 False.
> r.wait() # 等待任务完成, 返回任务执行结果.
> r.get() # 获取任务执行结果
> r.result # 任务执行结果.
> r.state # PENDING, START, SUCCESS
> r.status # PENDING, START, SUCCESS
# 使用 AsyncResult 方式获取执行结果.
# AsyncResult 主要用来存储任务执行信息与执行结果(类似 js 中的 Promise 对象),
> from celery.result import AsyncResult
> AsyncResult(task_id).get()
4
delay
task.delay(args1, args2, kwargs=value_1, kwargs2=value_2)
apply_async
delay 实际上是 apply_async 的别名, 还可以使用如下方法调用, 但是 apply_async 支持更多的参数:
task.apply_async(args=[arg1, arg2], kwargs={
key:value, key:value})
支持的参数
countdown : 等待一段时间再执行.
add.apply_async((2,3), countdown=5)
eta : 定义任务的开始时间
add.apply_async((2,3), eta=now+tiedelta(second=10))
expires : 设置超时时间.
add.apply_async((2,3), expires=60)
retry : 定时如果任务失败后, 是否重试.
add.apply_async((2,3), retry=False)
retry_policy : 重试策略.
自定义发布者,交换机,路由键, 队列, 优先级,序列方案和压缩方法:
task.apply_async((2,2), compression='zlib',
serialize='json',
queue='priority.high',
routing_key='web.add',
priority=0,
exchange='web_exchange')
from datetime import timedelta
from celery.schedules import crontab
BORKER_URL = 'redis://127.0.0.1:6379/1'
CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/2'
# 设置时区
CELERY_TIMEZONE = 'Asia/Shanghai'
# 导入指定的任务模块
CELERY_IMPORT = (
'celery_app.task1',
'celery_app.task2',
)
CELERYBEAT_SCHEDULE = {
# 每隔10秒钟执行任务
'task1':{
'task':'celery_app.task1.add',
'schedule':timedelta(seconds=10),
'args':(2,8)
},
# 每天的19:28分执行任务
'task2':{
'task':'celery_app.task2.multiply',
'schedule':crontab(hour=19,minute=28),
'args':(4,5)
}
}
# 使用
# Worker celery worker -A task -l INFO 启动worker
# Beat: celery beat -A task -l INFO 启动beat
# celery -B -A task worker -l INFO 同时启动beat worker
# celery worker --help
使用自定义调度类还可以实现动态添加任务. 使用 Django 可以通过 Django-celery 实现在管理后台创建,删除,更新任务, 是因为他使用了自定义的 调度类 djcelery.schedulers.DatabaseScheduler .
# 修改 tasks.py 文件.
from celery.utils.log import get_task_logger
logger = get_task_logger(__name__)
@app.task(bind=True)
def div(self, x, y):
logger.info(('Executing task id {0.id}, args: {0.args!r}'
'kwargs: {0.kwargs!r}').format(self.request))
try:
result = x/y
except ZeroDivisionError as e:
raise self.retry(exc=e, countdown=5, max_retries=3) # 发生 ZeroDivisionError 错误时, 每 5s 重试一次, 最多重试 3 次.
return result
当使用 bind=True 参数之后, 函数的参数发生变化, 多出了参数 self, 这这相当于把 div 编程了一个已绑定的方法, 通过 self 可以获得任务的上下文.
信号可以帮助我们了解任务执行情况, 分析任务运行的瓶颈. Celery 支持 7 种信号类型.
任务信号
应用信号
Worker 信号
Beat 信号
Eventlet 信号
日志信号
命令信号
不同的信号参数格式不同, 具体格式参见官方文档
# 在执行任务 add 之后, 打印一些信息.
@after_task_publish
def task_send_handler(sender=None, body=None, **kwargs):
print 'after_task_publish: task_id: {body[id]}; sender: {sender}'.format(body=body, sender=sender)
# tasks.py
class MyTask(Task):
def on_success(self, retval, task_id, args, kwargs):
print 'task done: {0}'.format(retval)
return super(MyTask, self).on_success(retval, task_id, args, kwargs)
def on_failure(self, exc, task_id, args, kwargs, einfo):
print 'task fail, reason: {0}'.format(exc)
return super(MyTask, self).on_failure(exc, task_id, args, kwargs, einfo)
# 正确函数, 执行 MyTask.on_success() :
@app.task(base=MyTask)
def add(x, y):
return x + y
# 错误函数, 执行 MyTask.on_failure() :
@app.task #普通函数装饰为 celery task
def add(x, y):
raise KeyError
return x + y
Flower 的 supervisor 管理配置文件:
[program:flower]
command=/opt/PyProjects/venv/bin/flower -A celery_worker:celery --broker="redis://localhost:6379/2" --address=0.0.0.0 --port=5555
directory=/opt/PyProjects/app
autostart=true
autorestart=true
startretries=3
user=derby
stdout_logfile=/var/logs/%(program_name)s.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=30
stderr_logfile=/var/logs/%(program_name)s-error.log
stderr_logfile_maxbytes=50MB
stderr_logfile_backups=3