Celery是异步消息队列, 可以在很多场景下进行灵活的应用.消息中包含了执行任务所需的的参数,用于启动任务执行, suoy所以消息队列也可以称作

在web应用开发中, 用户触发的某些事件需要较长事件才能完成. 可以将任务交给celery去执行, 待任务完成后再将结果返回给用户. 用户同步请求触发的其它任务, 如发送邮件,请求云服务等也可以交由celery来完成.

celery的另一个重要应用场景则是各种计划任务.

celery由5个主要组件组成:

  • producer: 任务发布者, 通过调用API向celery发布任务的程序, 如web后端的控制器.

  • celery beat: 任务调度, 根据配置文件发布定时任务

  • worker: 实际执行任务的程序

  • broker: 消息代理, 接受任务消息,存入队列再按顺序分发给worker执行.

  • result backend: 存储结果的服务器, 一般为各种数据库服务

整体结构如图所示:

broker是celery的关键组件, 目前的可靠选择有RabbitMQ和Redis, 出于稳定性等原因我们选择官方推荐的RabbitMQ作为broker.顺便安装librabbitmq作为RabbitMQ的python客户端.

消息的发送接受过程需要对序列进行序列化和反序列化, 从celery3.2开始官方出于安全性原因拒绝使用python内置的pickle作为序列化方案, 目前celery支持的序列化方案包括:

  • json: 跨语言的序列化方案

  • yaml: 拥有更多数据类型, 但python客户端性能不如json

  • msgpack: 二进制序列化方案, 比json更小更快

若对可读性有要求可以采用json方案, 若追求更高的性能则可以选用msgpack.

result backend用于存储异步任务的结果和状态, 目前可用的有Redis、RabbitMQ、MongoDB、Django ORM、SQLAlchemy等.

可以使用boundless方式安装依赖:

pip install "celery[librabbitmq,redis,msgpack]"

第一个异步任务

创建tasks.py文件, 并写入:

from celery import Celery

app = Celery('tasks', broker='redis://127.0.0.1:6379/0', backend='redis://127.0.0.1:6379/1')

@app.task
def add(x, y):
return x + y

这样我们创建了celery实例, Celery()的第一个参数为当前module的名称(py文件名或包名).

在终端执行命令以启动服务器:

celery -A tasks worker -l info

-A tasks 参数指定app为模块tasks,-l info参数指定log级别为info.

当看到这条log时说明celery已就绪:

[2016-09-11 18:04:43,758: WARNING/MainProcess] celery@finley-pc ready.

在python中导入任务并执行

>>> from tasks import add
>>> result = add.delay(1,2)
>>> result.result
3
>>> result.status
'SUCCESS'
>>> result.successful()
True

使用一个py文件作为module非常不便, 在更复杂的任务中可以采用python包作为module.

建立python包demo,建立下列文件:

app.py:

from celery import Celery

app = Celery('demo', include=['demo.tasks'])

app.config_from_object('demo.config')

app.start()

config.py

BROKER_URL = 'redis://127.0.0.1:6379/0'

CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/1'

CELERY_TASK_SERIALIZER = 'msgpack'

CELERY_RESULT_SERIALIZER = 'json'

CELERY_TASK_RESULT_EXPIRES = 60 * 60 * 24

CELERY_ACCEPT_CONTENT = ['json', 'msgpack']

tasks.py

from demo.app import app

@app.task
def add(x, y):
return x + y

在终端中启动:

 celery -A demo.app worker -l info

若将-A参数设为demo则会默认尝试启动demo.celery.因为该module与celery重名可能在导入时出现错误, 所以我们没有采用这种做法.

celery还支持绑定,日志,重试等特性:

from celery.utils.log import get_task_logger

logger = get_task_logger(__name__)

@app.task(bind=True)
def div(self, x, y):
logger.info('doing div')
try:
result = x / y
except ZeroDivisionError as e:
raise self.retry(exc=e, countdown=5, max_retries=3)
return result

bind=true将app对象作为self参数传给task函数.

前文的示例需要producer主动检查任务的状态,存在诸多不便. 我们可以在task函数中主动通知producer:

from celery import Celery
from demo.app import app
from urllib.request import urlopen @app.task
def add(x, y):
result = x + y
url = 'http://producerhost/callback/add?result=%d' % result
urlopen(url)
return result

上述示例中我们使用GET请求将结果发送给了producer的回调API, 当然很多情况下可以直接调用回调函数.

Celery易于与Web框架集成, 作者常采用的交互逻辑是:

  • 提供提交任务, 查询任务结果两个API, 由客户端决定何时查询结果

  • 采用websocket等技术, 服务器主动向客户端发送结果

当然也可以采用异步IO模式, 这需要一些扩展包的协助:

安装tornado-celery: pip install torando-celery

编写handler:

import tcelery
tcelery.setup_nonblocking_producer() from demo.tasks import add calss Users(RequestHandler):
@asynchronous
def get():
add.apply_async(args=[1,2], callback=self.on_success) def on_success(self, response):
users = response.result
self.write(users)
self.finish()

其它的Web框架也有自己的扩展包:

计划任务

celery的计划任务有schedule和crontab两种方式.

在config.py中添加配置:

CELERYBEAT_SCHEDULE = {

    'add': {

		'task': 'demo.tasks.add',

		'schedule': timedelta(seconds=10),

		'args': (16, 16)

    }
}

启动beat:

celery beat -A demo.app

然后启动worker:

celery -A demo.app worker -l info

或者与celery app一同启动:

celery -B -A demo.app worker -l info

'schedule'可以接受datetime, timedelta或crontab对象:

from celery.schedules import crontab

{
'schedule': crontab(hour=7, minute=30, day_of_week=1),
pass
}

webhook

上文中我们使用本地python函数作为worker, webhook机制允许使用远程的Web服务作为worker.

在使用webhook作为worker时, broker将消息封装为http请求发送给worker, 并按照协议解析返回值.

使用webhook需要在CELERY_IMPORTS参数中包含celery.task.http, 或者在启动参数中指定-I celery.task.http.

broker使用GET或POST方法发送请求, 参数由调用时的关键字参数指定. worker返回json格式的响应:

{'status': 'success', 'retval': ...}

在失败时返回响应:

{'status': 'failure', 'reason': ...}

我们用django作为worker:

from django.http import HttpResponse
import json def add(request):
x = int(request.GET['x'])
y = int(request.GET['y'])
result = x + y
response = {'status': 'success', 'retval': result}
return HttpResponse(json.dumps(response), mimetype='application/json')

配置django为http://cloudservice/webhook/add提供Web服务.

从本地添加任务:

>>>from celery.task.http import URL
>>>result = URL('http://cloudservice/webhook/add').get_async(x=10, y=10)
>>>result.get()
20

URL是HttpDispatchTask的快捷方法(shortcut):

>>> from celery.task.http import HttpDispatchTask
>>> res = HttpDispatchTask.delay(
... url='http://cloudservice/webhook/add',
... method='GET', x=10, y=10)
>>> res.get()
20

更多关于celery的内容请参阅:

Celery latest documentation

异步消息队列Celery的更多相关文章

  1. 异步分布式队列Celery

    异步分布式队列Celery 转载地址 Celery 是什么? 官网 Celery 是一个由 Python 编写的简单.灵活.可靠的用来处理大量信息的分布式系统,它同时提供操作和维护分布式系统所需的工具 ...

  2. C#实现异步消息队列

    原文:C#实现异步消息队列 拿到新书<.net框架设计>,到手之后迅速读了好多,虽然这本书不像很多教程一样从头到尾系统的讲明一些知识,但是从项目实战角度告诉我们如何使用我们的知识,从这本书 ...

  3. 八.利用springAMQP实现异步消息队列的日志管理

    经过前段时间的学习和铺垫,已经对spring amqp有了大概的了解.俗话说学以致用,今天就利用springAMQP来完成一个日志管理模块.大概的需求是这样的:系统中有很多地方需要记录操作日志,比如登 ...

  4. C#后台异步消息队列实现

    简介 基于生产者消费者模式,我们可以开发出线程安全的异步消息队列. 知识储备 什么是生产者消费者模式? 为了方便理解,我们暂时将它理解为垃圾的产生到结束的过程. 简单来说,多住户产生垃圾(生产者)将垃 ...

  5. 【Redis】redis异步消息队列+Spring自定义注解+AOP方式实现系统日志持久化

    说明: SSM项目中的每一个请求都需要进行日志记录操作.一般操作做的思路是:使用springAOP思想,对指定的方法进行拦截.拼装日志信息实体,然后持久化到数据库中.可是仔细想一下会发现:每次的客户端 ...

  6. 消息队列&Celery&RabbitMQ&zeromq

    一.消息队列 什么是消息队列? “消息队列”是在消息的传输过程中保存消息的容器. “消息”是在两台计算机间传送的数据单位.消息可以非常简单,例如只包含文本字符串:也可以更复杂,可能包含嵌入对象. 消息 ...

  7. 三.RabbitMQ之异步消息队列(Work Queue)

    上一篇文章简要介绍了RabbitMQ的基本知识点,并且写了一个简单的发送和接收消息的demo.这一篇文章继续介绍关于Work Queue(工作队列)方面的知识点,用于实现多个工作进程的分发式任务. 一 ...

  8. 分布式消息队列 Celery 的最佳实践

    目录 目录 不使用数据库作为 Broker 不要过分关注任务结果 实现优先级任务 应用 Worker 并发池的动态扩展 应用任务预取数 保持任务的幂等性 应用任务超时限制 善用任务工作流 合理应用 a ...

  9. celery异步消息队列的使用

    1.准备工作 1.1 流程图 2.环境安装 2.1.在Ubuntu中需要安装redis 安装redis $sudo apt-get update $sudo apt-get install redis ...

随机推荐

  1. Python自动化开发 - 常用模块(一)

    本节内容 1.模块介绍 2.time&datetime模块 3.random模块 4.os模块 5.sys模块 6.json&pickle模块 7.logging模块 一.模块介绍 模 ...

  2. Delphi-idHttp-Post JSON用法 good

    从国外网站抄来的代码 Delphi source: http := TIdHttp.Create(nil);http.HandleRedirects := True;//允许头转向http.ReadT ...

  3. 直接端口打印 支持USB接口的打印机吗?解决办法

    直接端口打印 支持USB接口的打印机吗?解决办法 www.MyException.Cn  网友分享于:2013-09-15  浏览:488次       直接端口打印 支持USB接口的打印机吗?问题如 ...

  4. PHP内存溢出Allowed memory size of 解决办法

    PHP内存溢出Allowed memory size of 解决办法 博客分类: php   ============================Allowed memory size of  x ...

  5. Android-多线程安全问题-synchronized

    先看一个售票案例Demo,多线程程序对共享数据操作引发的安全问题: package android.java.thread09; /** * 售票线程 */ class Booking impleme ...

  6. C# 用户选择单个压缩-系统自带压缩

    //用C#自带的压缩,最少要.net4.5或以上,先增加引用 System.IO.Compression.FileSystem // FolderBrowserDialog dlg = new Fol ...

  7. DEV通过FindFilterText自动检索gridview内容

    private void TreeView1_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) { if (names!=nul ...

  8. 迁移桌面程序到MS Store(2)——Desktop App Converter

    迁移传统桌面程序到MS Store的另一种方式是使用Desktop App Converter工具.虽然本篇标题包含了Desktop App Converter(以下简称DAC),实际上我是来劝你别用 ...

  9. DataFrame 操作

    # 删除td数据框中的 指定列 td.drop(columns=['ship-city'],axis=1,inplace=True) #删除指定列 并且修改掉原始数据 # 删除td数据框中的 最后一列 ...

  10. 【BZOJ2595】 [Wc2008]游览计划

    BZOJ2595 [Wc2008]游览计划 Solution 考虑这是一个最小费用连通性的问题,既然大家都说这是什么斯坦纳树那就是的吧... 所以我们肯定可以这样设一个dp状态: \(dp_{i,j, ...