Celery是一个功能完备即插即用的异步任务队列系统。它适用于异步处理问题,当发送邮件、或者文件上传, 图像处理等等一些比较耗时的操作,我们可将其异步执行,这样用户不需要等待很久,提高用户体验。
简单,易于使用和维护,有丰富的文档。
高效,单个celery进程每分钟可以处理数百万个任务。
灵活,celery中几乎每个部分都可以自定义扩展。
任务队列是一种跨线程、跨机器工作的一种机制. 任务队列中包含称作任务的工作单元。有专门的工作进程持续不断的监视任务队列,并从中获得新的任务并处理. celery通过消息进行通信,通常使用一个叫Broker(中间人)来协client(任务的发出者)和worker(任务的处理者).
pip install celery
pip install redis
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import absolute_import
from celery import Celery
app = Celery('mycelery', include=['mycelery.tasks'])
app.config_from_object('mycelery.config')
if __name__ == '__main__':
app.start()
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import absolute_import
# 使用redis作为任务队列
# 在celery4.x以后,就是BROKER_URL,如果是以前,需要写成CELERY_BROKER_URL
# 无密码:BROKER_URL = 'redis://127.0.0.1:6379/0'
BROKER_URL = 'redis://username:[email protected]:6379/0'
# 使用redis存储任务执行结果,默认不使用,(如果涉及有返回值的函数情况下使用)
CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/1'
# 设置时区
CELERY_TIMEZONE = 'Asia/Shanghai'
# 启动时区设置
CELERY_ENABLE_UTC = True
# 并发的worker数量,也是命令行-c指定的数目,默认是服务器的内核数目
# 事实上并不是worker数量越多越好,保证任务不堆积,加上一些新增任务的预留就可以了
CELERYD_CONCURRENCY = 10
# celery worker每次去redis取任务的数量,默认值就是4
CELERYD_PREFETCH_MULTIPLIER = 4
# 每个worker执行了多少次任务后就会死掉,建议数量大一些,默认是无限的
CELERYD_MAX_TASKS_PER_CHILD = 200
# celery任务执行结果的超时时间
CELERY_TASK_RESULT_EXPIRES = 60 * 20
# 单个任务的运行时间限制,否则会被杀死
CELERYD_TASK_TIME_LIMIT = 60
# 指定任务接受的序列化类型.
CELERY_ACCEPT_CONTENT = ['application/json']
# 将任务结果使用'pickle'序列化成'json'格式
# 任务序列化方式
CELERY_TASK_SERIALIZER = 'json'
# 任务执行结果序列化方式
CELERY_RESULT_SERIALIZER = 'json'
# 也可以直接在Celery对象中设置序列化方式
app = Celery('tasks', broker='redis://127.0.0.1:6379/1', task_serializer='yaml')
# 常见的数据序列化方式
binary: 二进制序列化方式;python的pickle默认的序列化方法;
json:json 支持多种语言, 可用于跨语言方案,但好像不支持自定义的类对象;
XML:类似标签语言;
msgpack:二进制的类 json 序列化方案, 但比 json 的数据结构更小, 更快;
yaml:yaml 表达能力更强, 支持的数据类型较 json 多, 但是 python 客户端的性能不如 json
# 任务发送完成是否需要确认,对性能会稍有影响
CELERY_ACKS_LATE = True
# 压缩方案选择,可以是zlib, bzip2,默认是发送没有压缩的数据
CELERY_MESSAGE_COMPRESSION = 'zlib'
# 限制任务的执行频率
# 下面这个就是限制tasks模块下的add函数,每秒钟只能执行10次
CELERY_ANNOTATIONS = {'tasks.add':{'rate_limit':'10/s'}}
# 或者限制所有的任务的刷新频率
CELERY_ANNOTATIONS = {'*':{'rate_limit':'10/s'}}
# 也可以设置如果任务执行失败后调用的函数
def my_on_failure(self,exc,task_id,args,kwargs,einfo):
print('task failed')
CELERY_ANNOTATIONS = {'*':{'on_failure':my_on_failure}}
# 间隔任务
# 定时任务
CELERYBEAT_SCHEDULE = {
'add-every-30-seconds': {
'task': 'mycelery.tasks.add',
'schedule': timedelta(seconds=30),
'args': (16, 16)
},
# Executes every Monday morning at 7:30 A.M
'add-every-monday-morning': {
'task': 'tasks.add',
'schedule': crontab(hour=7, minute=30, day_of_week=1),
'args': (16, 16),
},
}
#设置默认的队列名称,如果一个消息不符合其他的队列就会放在默认队列里面,如果什么都不设置的话,数据都会发送到默认的队列中
CELERY_DEFAULT_QUEUE = "default"
# 或者配置成下面两种方式:
# 配置队列(settings.py)
CELERY_QUEUES = (
Queue('default',
Exchange('default'),
routing_key='default'),
Queue('for_task_collect',
Exchange('for_task_collect'),
routing_key='for_task_collect'),
Queue('for_task_compute',
Exchange('for_task_compute'),
routing_key='for_task_compute'),
)
CELERY_QUEUES = {
"default": { # 这是上面指定的默认队列
"exchange": "default",
"exchange_type": "direct",
"routing_key": "default"
},
"topicqueue": { # 这是一个topic队列 凡是topictest开头的routing key都会被放到这个队列
"routing_key": "topic.#",
"exchange": "topic_exchange",
"exchange_type": "topic",
},
"task_eeg": { # 设置扇形交换机
"exchange": "tasks",
"exchange_type": "fanout",
"binding_key": "tasks",
},
}
#路由(哪个任务放入哪个队列)
CELERY_ROUTES = {
'umonitor.tasks.multiple_thread_metric_collector':
{
'queue': 'for_task_collect',
'routing_key': 'for_task_collect'
},
'compute.tasks.multiple_thread_metric_aggregate':
{
'queue': 'for_task_compute',
'routing_key': 'for_task_compute'
},
'compute.tasks.test':
{
'queue': 'for_task_compute',
'routing_key': 'for_task_compute'
},
}
# Exchange的值为:
1、fanout(广播式),它不需要指定路由就会把所有发送到该Exchange的消息路由到所有与它绑定的Queue中
例如:
CELERY_QUEUES = (
Queue('for_adds',Exchange('for_adds',type='fanout')),
Queue('for_send_emails', Exchange('for_adds',type='fanout')),
Queue('add', Exchange('for_adds',type='fanout')),
)
CELERY_ROUTES = {
'celery_test.tasks.add': {'exchange':'for_adds'},
'celery_test.tasks.send_mail': {'exchange':'for_adds',},
'celery_test.tasks.adds': {'exchange':'for_adds',},
}
2、direct,direct类型的Exchange路由规则也很简单,它会把消息路由到那些binding key与routing key完全匹配的Queue中
例如:
CELERY_QUEUES = (
Queue('for_adds',Exchange('for_adds',type='direct'), routing_key='adds'),
Queue('for_send_emails', Exchange('for_adds',type='direct'), routing_key='email'),
Queue('add', Exchange('for_adds',type='direct'), routing_key='add'),
)
CELERY_ROUTES = {
'celery_test.tasks.add': {'exchange':'for_adds','routing_key':'add'},
'celery_test.tasks.send_mail': {'exchange':'for_adds','routing_key':'email'},
'celery_test.tasks.adds': {'exchange':'for_adds','routing_key':'add'},
}
3、topic,topic类型的Exchange在匹配规则上进行了扩展,它与direct类型的Exchage相似,也是将消息路由到binding key与routing key相匹配的Queue中,但这里的匹配规则有些不同,它约定
3.1 routing key为一个句点号“. ”分隔的字符串(我们将被句点号“. ”分隔开的每一段独立的字符串称为一个单词),如“*.task.*”、"*.*.email"、“*.add”
3.2 binding key与routing key一样也是句点号“. ”分隔的字符串
3.3 binding key中可以存在两种特殊字符“*”与“#”,用于做模糊匹配,其中“*”用于匹配一个单词,“#”用于匹配多个单词(可以是零个)
例如:
CELERY_QUEUES = (
Queue('for_adds',Exchange('for_adds',type='topic'), routing_key='*.task.*'),
Queue('for_send_emails', Exchange('for_adds',type='topic'), routing_key='*.*.email'),
Queue('add', Exchange('for_adds',type='topic'), routing_key='*.add'),
)
CELERY_ROUTES = {
'celery_test.tasks.add': {'exchange':'for_adds','routing_key':'q.task.email'},
'celery_test.tasks.send_mail': {'exchange':'for_adds','routing_key':'a.task.e'},
'celery_test.tasks.adds': {'exchange':'for_adds','routing_key':'b.add'},
}
该路由的设置,adds会到add队列,send_mail会到for_adds队列,add会到for_adds和for_send_emails队列
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import absolute_import
from mycelery.celery import app
@app.task
def add(x, y):
return x + y
# 1. 声明一个和celery一模一样的任务函数,导包来解决
from mycelery.sms.tasks import send_sms
# 2. 调用任务函数,发布任务
send_sms.delay(mobile,code)
# send_sms.delay() 如果调用的任务函数没有参数,则不需要填写任何内容
celery -A mycelery.main worker --loglevel=info
# 定时任务
celery -A mycelery.main worker -B -l info
# windows
celery -A mycelery.main worker --pool=solo -l info
# 其他参数
multi start #后台启动
-c 4 # 工人数量
# 任务清除
celery purge
# 启动不同worker处理不同队列的任务
# 指定worker_compute去处理队列for_task_compute的任务
python manage.py celery worker -E -l INFO -n worker_compute -Q for_task_compute
# 指定worker_collect去处理队列for_task_collect的任务
python manage.py celery worker -E -l INFO -n worker_collect -Q for_task_collect
celery.py加上
# 把celery和django进行组合,识别和加载django的配置文件
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'api.settings.dev')