• 异步消息队列Celery


    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

  • 相关阅读:
    sysbench 安装遇到的问题
    Mysql对用户操作加审计功能——高级版
    Mysql对用户操作加审计功能——初级版
    最近身体越来越差了
    ORCHARD学习教程-安装
    ORCHARD学习教程-介绍
    SharePoint配置网站集的审核设置
    Sharepoint 文档知识管理系统--Word在试图打开文件时遇到错误
    Python&Django学习系列之-激活管理界面
    Flutter
  • 原文地址:https://www.cnblogs.com/Finley/p/5862852.html
Copyright © 2020-2023  润新知