任务队列神器:Celery 入门到进阶指南
任务队列神器:Celery 入门到进阶指南
1.什么是celery
celery是一个简单,灵活、可靠的分布式任务执行框架,可以支持大量任务的并发执行。celery采用典型生产者和消费者模型。生产者提交任务到任务队列,众多消费者从任务队列中取任务执行。
1.1 celery架构
Celery由以下三部分构成:消息中间件(Broker)、任务执行单元Worker、结果存储(Backend)
- 任务调用提交任务执行请求给Broker队列
- 如果是异步任务,worker会立即从队列中取出任务并执行,执行结果保存在Backend中
- 如果是定时任务,任务由Celery Beat进程周期性地将任务发往Broker队列,Worker实时监视消息队列获取队列中的任务执行
1.2 应用场景
- 大量的长时间任务的异步执行, 如上传大文件
- 大规模实时任务执行,支持集群部署,如支持高并发的机器学习推理
- 定时任务执行,如定时发送邮件,定时扫描机器运行情况
2.安装
celery安装非常简单, 除了安装celery,本文中使用redis作为消息队列即Broker
# celery 安装
pip install celery
# celery 监控 flower
pip install flower
pip install redis
# redis 安装
yum install redis
# redis启动
redis-server /etc/redis.conf
3. 完整例子
celery的应用开发涉及四个部分
- celery 实例初始化
- 任务的定义(定时和实时任务)
- 任务worker的启动
- 任务的调用
3.1 项目目录
# 项目目录
wedo
.
├── config.py
├── __init__.py
├── period_task.py
└── tasks.py
3.2 celery 实例初始化
celery的实例化,主要包括执行Broker和backend的访问方式,任务模块的申明等
# celery 实例初始化
# __init__.py
from celery import Celery
app = Celery('wedo') # 创建 Celery 实例
app.config_from_object('wedo.config')
# 配置 wedo.config
# config.py
BROKER_URL = 'redis://10.8.238.2:6379/0' # Broker配置,使用Redis作为消息中间件
CELERY_RESULT_BACKEND = 'redis://10.8.238.2:6379/0' # BACKEND配置,这里使用redis
CELERY_RESULT_SERIALIZER = 'json' # 结果序列化方案
CELERY_TASK_RESULT_EXPIRES = 60 * 60 * 24 # 任务过期时间
CELERY_TIMEZONE='Asia/Shanghai' # 时区配置
CELERY_IMPORTS = ( # 指定导入的任务模块,可以指定多个
'wedo.tasks',
'wedo.period_task'
)
3.3 任务的定义
celery中通过@task的装饰器来进行申明celery任务,其他操作无任何差别
# 任务的定义
# 简单任务 tasks.py
import celery
import time
from celery.utils.log import get_task_logger
from wedo import app
@app.task
def sum(x, y):
return x + y
@app.task
def mul(x, y):
time.sleep(5)
return x * y
定时任务和实时任务的区别主要是要申明何时执行任务,任务本身也是通过task装饰器来申明 何时执行任务有2种
- 指定频率执行:sender.add_periodic_task(时间频率单位s, 任务函数, name='to_string')
- crontab方式:分钟/小时/天/月/周粒度, 可以支持多种调度
# 任务的定义
# 定时任务 period_task.py
from wedo import app
from celery.schedules import crontab
@app.on_after_configure.connect
def setup_periodic_tasks(sender, **kwargs):
sender.add_periodic_task(5.0, to_string.s("celery peroid task"), name='to_string') # 每5秒执行add
sender.add_periodic_task(
crontab(minute='*/10'), #每10分钟执行一次
send_mail.s('hello, this is a celery'), name='send_mail'
)
@app.task
def send_mail(content):
print('send mail, content is %s' % content)
@app.task
def to_string(text):
return 'this is a %s' % text
3.4 任务worker的启动
任务启动分为worker启动和定时任务beat启动
# -A wedo为应用模块
# -l为日志level
# -c 为进程数
celery worker -A wedo -l debug -c 4
# 后台启动
nohup celery worker -A wedo -l debug -c 4 > ./log.log 2>&1
# 从下面的日志可以看出启动了4个任务
# . wedo.period_task.send_mail
# . wedo.period_task.to_string
# . wedo.tasks.mul
# . wedo.tasks.sum
-------------- celery@localhost.localdomain v4.4.2 (cliffs)
--- ***** -----
-- ******* ---- Linux-3.10.0-327.28.3.el7.x86_64-x86_64-with-centos-7.2.1511-Core 2020-04-25 23:35:26
- *** --- * ---
- ** ---------- [config]
- ** ---------- .> app: wedo:0x7f05af30d320
- ** ---------- .> transport: redis://10.8.238.2:6379/0
- ** ---------- .> results: redis://10.8.238.2:6379/0
- *** --- * --- .> concurrency: 4 (prefork)
-- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
--- ***** -----
-------------- [queues]
.> celery exchange=celery(direct) key=celery
[tasks]
. celery.accumulate
. celery.backend_cleanup
...
. wedo.period_task.send_mail
. wedo.period_task.to_string
. wedo.tasks.mul
. wedo.tasks.sum
...
[2020-04-25 23