任务队列神器:Celery 入门到进阶指南

发布于2021-03-24 16:24:53阅读 1.9K0
 

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:35:27,617: INFO/MainProcess] celery@localhost.localdomain ready.
[2020-04-25 23:35:27,617: DEBUG/MainProcess] basic.qos: prefetch_count->16
[2020-04-25 23:35:27,655: DEBUG/MainProcess] celery@12103675 joined the party

复制

celery beat -A wedo.period_task

celery beat v4.4.2 (cliffs) is starting.
__ - ... __ - _
LocalTime -> 2020-04-25 23:37:08
Configuration ->
. broker -> redis://10.8.238.2:6379/0
. loader -> celery.loaders.app.AppLoader
. scheduler -> celery.beat.PersistentScheduler
. db -> celerybeat-schedule
. logfile -> [stderr]@%WARNING
. maxinterval -> 5.00 minutes (300s)

复制

# worker启动是4个进程
\_ /root/anaconda3/envs/post/bin/celery worker -A wedo -l debug -c 4
\_ /root/anaconda3/envs/post/bin/celery worker -A wedo -l debug -c 4
\_ /root/anaconda3/envs/post/bin/celery worker -A wedo -l debug -c 4
\_ /root/anaconda3/envs/post/bin/celery worker -A wedo -l debug -c 4
\_ /root/anaconda3/envs/post/bin/celery worker -A wedo -l debug -c 4

复制

worker和beat的停止

ps auxww | awk '/celery worker/ {print $2}' | xargs kill -9
ps auxww | awk '/celery beat/ {print $2}' | xargs kill -9

复制

3.5 任务的调用

任务worker已经启动好了,通过任务调用传递给broker(redis),并返回任务执行结果 任务调用主要有两种,本质是一致的,delay是apply_async的封装,apply_async可以支持更多的任务调用配置

  • task.apply_async(args=[arg1, arg2], kwargs={'kwarg1': 'x', 'kwarg2': 'y'})
  • task.delay(arg1, arg2, kwarg1='x', kwarg2='y')

apply_async和delay会返回一个异步的任务结果,AsyncResult中存储了任务的执行状态和结果,常用的操作

value = result.get() # 任务返回值
print(result.__dict__) # 结果信息
print(result.successful()) # 是否成功
print(result.fail()) # 是否失败
print(result.ready()) # 是否执行完成
print(result.state) # 状态 PENDING -> STARTED -> SUCCESS/FAIL

复制

常规任务:

from celery.utils.log import get_logger
from wedo.tasks import sum, mul, post_file
from celery import group, chain, chord
logger = get_logger(__name__)
try:
result = mul.apply_async(args=(2, 2))
value = result.get() # 等待任务执行完毕后,才会返回任务返回值
print(value)
except mul.OperationalError as exc: # 任务异常处理
logger.exception('Sending task raised: %r', exc)

复制

组合任务:

  • 多个任务并行执行, group
  • 多个任务链式执行,chain:第一个任务的返回值作为第二个的输入参数,以此类推
result = group(sum.s(i, i) for i in range(5))()
result.get()
# [0, 2, 4, 6, 8]
result = chain(sum.s(1,2), sum.s(3), mul.s(3))()
result.get()
# ((1+2)+3)*3=18

复制

4. 分布式集群部署

celery作为分布式的任务队列框架,worker是可以执行在不同的服务器上的。部署过程和单机上启动是一样。只要把项目代码copy到其他服务器,使用相同命令就可以了。可以思考下,这个是怎么实现的?对了,就是通过共享Broker队列。使用合适的队列,如redis,单进程单线程的方式可以有效的避免同个任务被不同worker同时执行的情况。

celery worker -A wedo  -l debug -c 4

复制

  • 分布式集群如下:

5. 进阶使用

在前面已经了解了celery的主要的功能了。celery还为一些特别的场景提供了需要扩展的功能

5.1 任务状态跟踪和日志

有时候我们需要对任务的执行情况做一些监控,比如失败后报警通知。

  • celery在装饰器@app.task中提供了base参数,传入重写的Task模块,重新on_*函数就可以控制不同的任务结果
  • 在@app.task提供bind=True,可以通过self获取Task中各种参数
    • self.request:任务的各种参数
    • self.update_state: 自定义任务状态, 原有的任务状态:PENDING -> STARTED -> SUCCESS, 如果你想了解STARTED -> SUCCESS之间的一个状态,比如执行的百分比之类,可以通过自定义状态来实现
    • self.retry: 重试
import celery
import time
from celery.utils.log import get_task_logger
from wedo import app logger = logger = get_task_logger(__name__)
class TaskMonitor(celery.Task):
def on_failure(self, exc, task_id, args, kwargs, einfo):
"""failed callback"""
logger.info('task id: {0!r} failed: {1!r}'.format(task_id, exc)) def on_success(self, retval, task_id, args, kwargs):
"""success callback"""
logger.info('task id:{} , arg:{} , successful !'.format(task_id,args)) def on_retry(self, exc, task_id, args, kwargs, einfo):
"""retry callback"""
logger.info('task id:{} , arg:{} , retry ! einfo: {}'.format(task_id, args, exc)) @app.task(base=TaskMonitor, bind=True, name='post_file')
def post_file(self, file_names):
logger.info(self.request.__dict__)
try:
for i, file in enumerate(file_names):
print('the file %s is posted' % file)
if not self.request.called_directly:
self.update_state(state='PROGRESS',
meta={'current': i, 'total': len(file_names)})
time.sleep(2)
except Exception as exec:
raise self.retry(exc=exec, countdown=3, max_retries=5)

复制

5.2 任务指定特定的worker执行

celery做为支持分布式,理论上可以无限扩展worker。默认情况下celery提交任务后,任务会放入名为celery的队列,所有在线的worker都会从任务队列中获取任务,任一个worker都有可能执行这个任务。有时候,有时候任务的特殊性或者机器本身的限制,某些任务只能跑在某些worker上。celery提供了queue在区别不同的worker,很好的支持这种情况。

  • 启动worker时,-Q 指定worker支持的任务列队名, 可以支持多个队列名哦
celery worker -A wedo  -l debug -c 4 -Q celery,hipri

复制

  • 任务调用时, queue=*来指定需要执行worker
result = mul.apply_async(args=(2, 2), queue='hipri')

复制

6. 任务队列监控

如果你想通过可视化的方式,查看celery的一切。flower提供可行的解决方案,十分的方便

flower -A wedo --port=6006
# web访问 http://10.8.238.2:6006/

复制

7. 总结

本文和大家了介绍了分布式的队列celery, 妥妥的很全吧, 欢迎交流。总结下内容:

  • celery为分布式队列, 通过消息队列连接任务提交和执行者worker, 松耦合模式,可扩展
  • celery消息队列建议为redis
  • celery通过@app.task装饰把普通任务变成celery Task
  • celery worker 通过不同queue支持特定的worker消费特定的任务
  • @app.task中可以同步base和bind参数获取更过的控制任务生命周期
  • flower监控celery全过程
  • celery doc:https://docs.celeryproject.org/en/master/getting-started/index.html

任务队列神器:Celery 入门到进阶指南的更多相关文章

  1. Weex入门与进阶指南

    Weex入门与进阶指南 标签: WeexiOSNative 2016-07-08 18:22 59586人阅读 评论(8) 收藏 举报 本文章已收录于:  iOS知识库  分类: iOS(87)  职 ...

  2. Java 服务端入门和进阶指南

    作者:谢龙 链接:https://www.zhihu.com/question/29581524/answer/44872235 来源:知乎 著作权归作者所有,转载请联系作者获得授权. 现在互联网上资 ...

  3. 分布式任务队列Celery入门与进阶

    一.简介 Celery是由Python开发.简单.灵活.可靠的分布式任务队列,其本质是生产者消费者模型,生产者发送任务到消息队列,消费者负责处理任务.Celery侧重于实时操作,但对调度支持也很好,其 ...

  4. Python语言学习之Python入门到进阶

    人们常说Python语言简单,编写简单程序时好像也确实如此.但实际上Python绝不简单,它也是一种很复杂的语言,其功能特征非常丰富,能支持多种编程风格,在几乎所有方面都能深度定制.要想用好Pytho ...

  5. 服务端工程师入门与进阶 Java 版

    前言 欢迎加入我们.这是一份针对实习生/毕业生的服务端开发入门与进阶指南.遇到问题及时问你的 mentor 或者直接问我. 建议: 尽量用google查找技术资料. 有问题在stackoverflow ...

  6. 异步任务神器 Celery-入门

    一.Celery入门介绍 在程序的运行过程中,我们经常会碰到一些耗时耗资源的操作,为了避免它们阻塞主程序的运行,我们经常会采用多线程或异步任务.比如,在 Web 开发中,对新用户的注册,我们通常会给他 ...

  7. 异步任务神器 Celery 简明笔记

    转自:http://www.jianshu.com/p/1840035cb510 异步任务 异步任务是web开发中一个很常见的方法.对于一些耗时耗资源的操作,往往从主应用中隔离,通过异步的方式执行.简 ...

  8. Wireshark入门与进阶系列(二)

    摘自http://blog.csdn.net/howeverpf/article/details/40743705 Wireshark入门与进阶系列(二) “君子生非异也,善假于物也”---荀子 本文 ...

  9. Celery入门指北

    Celery入门指北 其实本文就是我看完Celery的官方文档指南的读书笔记.然后由于我的懒,只看完了那些入门指南,原文地址:First Steps with Celery,Next Steps,Us ...

  10. 可怕!字节跳动大牛爆出的“Android进阶指南”,强到无法形容,GitHub已标星“8.5k”!

    前言 从去年8月份开始,我开始重新找工作,在这期间刷了不少面试题和大牛博客,无意中薅到一份字节跳动大牛整理的一份"Android进阶指南",包含Android面试常见问题.主流技术 ...

随机推荐

  1. overflow_auto在flex_1的容器失效

    旧文章从语雀迁移过来,原日期为2022-02-22 我们经常使用flex:1来动态分配父容器剩余空间,这时候如果要在容器上增加滚动条,使用overflow: auto可能会失效. 原因: 一般原因:因 ...

  2. DSS+Linkis Ansible 单机一键安装脚本

    DSS+Linkis Ansible 单机一键安装脚本 一.简介 为解决繁琐的部署流程,简化安装步骤,本脚本提供一键安装最新版本的DSS+Linkis环境:部署包中的软件采用我自己编译的安装包,并且为 ...

  3. 分享项目中在用的asp.net下载业务的服务端基类(支持客户端显示下载百分比进度,支持并发数控制,支持限速)

    /// <summary> /// 功能简介:asp.net的下载业务的服务端基类(支持客户端显示下载百分比进度,支持并发数控制,支持限速) /// 创建时间:2015-11-20 /// ...

  4. BBS项目(二): 登录功能 首页导航条搭建 首页主体部分 个人站点页面搭建 文章分类与标签 日期归档

    目录 登录功能 pillow模块生成验证码 前端发送ajax请求 后端auth模块校验 sweetalert弹窗提示登录失败 首页导航条搭建 修改密码 退出登录 首页主体部分 首页前端框架搭建 adm ...

  5. 03-Sed基础语法及例子

    1 Sed语法及举例 在实际使用sed过程中经常使用字符串的替换.删除.查找等操作.Linux中的编辑器Vi.GVIM.emacs等都可以进行上述操作,但是大量进行操作的时候,效率很低. 地址参数 { ...

  6. DVWA系列1:搭建 DVWA 环境

    DVWA系列1:搭建 DVWA 环境 DVWA 是一个合法的漏洞测试.学习环境,先引用一段官方的介绍: Damn Vulnerable Web Application (DVWA) is a PHP/ ...

  7. three.js一步一步来--如何用线画出一个面--网格板子

    网格板子~~~ <template> <div style="width:1000px; height:800px"> <p>网格布局</ ...

  8. 公司规定所有接口都用 post 请求,这正确么?

    目录 背景 get 与 post 的区别 所有接口都用 post 请求? 背景 最近在逛知乎的时候发现一个有趣的问题:公司规定所有接口都用 post 请求,这是为什么? 看到这个问题的时候其实我也挺有 ...

  9. 腾讯出品小程序自动化测试框架【Minium】系列(五)API详解(中)

    写在前面 又有好久没更新小程序自动化测试框架Minium系列文章了,主要真的太忙,尽量做到每周一更吧,还请大家能够理解! 上篇文章为大家分享关于Minium中Minium.App模块的API 使用,接 ...

  10. KingbaseES数据库目录结构

    KingbaseES数据库目录结构 [kingbase@postgres V8]$ tree -LP 2 data/ . ├── data │ ├── base # 存储用户创建的数据库文件及隶属于用 ...