Celery (1)- 安装配置及使用

简介

  • Celery是一个简单的/灵活且可靠的,处理大量消息的分布式系统
  • 专注于实时处理的异步任务队列
  • 同时也支持任务调度

架构图

Celery (1)- 安装配置及使用_第1张图片

组件介绍

  • Producer : 任务生产者. 调用 Celery API , 函数或者装饰器, 而产生任务并交给任务队列处理的都是任务生产者。

  • Broker : 消息代理, 队列本身. 也称为消息中间件. 接受任务生产者发送过来的任务消息, 存进队列再按序分发给任务消费方(通常是消息队列或者数据库). 通常用 RabbitMQ或者Redis

  • Celery Beat : 任务调度器. Beat 进程会读取配置文件的内容, 周期性的将配置中到期需要执行的任务发送给任务队列.例如如下配置的一个周期性执行的一个任务:

    CELERYBEAT_SCHEDULE = {
           
        'send_mail': {
           
            'task': 'work.notify.email.send_mail',
            # 'schedule': timedelta(minute=1),
            'schedule': crontab(minute='*/1'),
            'args': ('usr', 'sub', 'msg')
        }
    }
    

    任务调度主要是为了解决业务场景中定时或周期任务,分别使用timedelta和crontab来定义计划任务,crontab的精度无法精确到秒时可使用timedelta代替,CELERYBEAT_SCHEDULE下可以定义多个计划/周期任务,send_mail为任务名称,task为任务单元导入名,schedule为具体调度,args为任务单元的参数.

    运行时可先启动work进程池(celery worker -A work.app -l info)然后再启动beat进程池(celery beat -A work.app -l info),观察会发现beat进程每分钟生成一个任务,work进程发现任务后立即执行

  • Celery Worker : 执行任务的消费者, 通常会在多台服务器运行多个消费者, 提高运行效率.

  • Result Backend : 任务处理完成之后保存状态信息和结果, 以供查询.

依赖库
  • billiard : 基于 Python2.7 的 multisuprocessing 而改进的库, 主要用来提高性能和稳定性
  • librabbitmp : C 语言实现的 Python 客户端,
  • kombu : Celery 自带的用来收发消息的库, 提供了符合 Python 语言习惯的, 使用 AMQP 协议的高级借口.
消息代理

使用于生产环境的消息代理有 RabbitMQ 和 Redis, 官方推荐 RabbitMQ.

使用场景

  • 异步任务,将耗时操作提交给Celery去异步执行,比如发送短信/邮件/消息推送。音视频处理等等
  • 定时任务。类似于crontab,比如每日数据统计

序列化

方案 说明
pickle pickle 是Python 标准库中的一个模块, 支持 Pyuthon 内置的数据结构, 但他是 Python 的专有协议. Celery 官方不推荐.
son json 支持多种语言, 可用于跨语言方案.
yaml yaml 表达能力更强, 支持的数据类型较 json 多, 但是 python 客户端的性能不如 json
msgpack 二进制的类 json 序列化方案, 但比 json 的数据结构更小, 更快.

配置文件参数说明

配置项 说明
CELERY_DEFAULT_QUEUE 默认队列
CELERY_BROKER_URL Broker 地址
CELERY_RESULT_BACKEND 结果存储地址
CELERY_TASK_SERIALIZER 任务序列化方式
CELERY_RESULT_SERIALIZER 任务执行结果序列化方式
CELERY_TASK_RESULT_EXPIRES 任务过期时间
CELERY_ACCEPT_CONTENT 指定任务接受的内容类型(序列化)

代码示例:

# 安装
$ pip install celery, redis, msgpack
 
# 配置文件 celeryconfig.py
 
    CELERY_BROKER_URL = 'redis://localhost:6379/1'
    CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
    CELERY_TASK_SERIALIZER = 'json'
    CELERY_RESULT_SERIALIZER = 'json'
    CELERY_TASK_RESULT_EXPIRES = 60 * 60 * 24   # 任务过期时间
    CELERY_ACCEPT_CONTENT = ["json"]            # 指定任务接受的内容类型.
 
# 初始化文件 celery.py
    
    from __future__ import absolute_import
    from celery import Celery
 
    app = Celery('proj', include=["proj.tasks"])
    app.config_from_object("proj.celeryconfig")
 
    if __name__ == "__main__":
        app.start()     
 
# 任务文件 tasks.py
 
    from __future__ import absolute_import
    from proj.celery import app
 
    @app.task
    def add(x, y):
        return x + y    
 
# 启动消费者
    $ celery -A proj worker -l info
 
# 在终端中测试
    > from proj.tasks import add
    > r = add.delay(2,4)
    > r.result
      6
    > r.status
      u"SUCCESS"
    > r.successful()
      True
 
    > r.ready()     # 返回布尔值,  任务执行完成, 返回 True, 否则返回 False.
    > r.wait()      # 等待任务完成, 返回任务执行结果.
    > r.get()       # 获取任务执行结果
    > r.result      # 任务执行结果.
    > r.state       # PENDING, START, SUCCESS
    > r.status      # PENDING, START, SUCCESS
 
    # 使用 AsyncResult 方式获取执行结果.
    # AsyncResult 主要用来存储任务执行信息与执行结果(类似 js 中的 Promise 对象), 
    > from celery.result import AsyncResult
    > AsyncResult(task_id).get()
      4

任务调度的方法

  • delay

    task.delay(args1, args2, kwargs=value_1, kwargs2=value_2)
    
  • apply_async
    delay 实际上是 apply_async 的别名, 还可以使用如下方法调用, 但是 apply_async 支持更多的参数:

    task.apply_async(args=[arg1, arg2], kwargs={
           key:value, key:value})
    

    支持的参数

    • countdown : 等待一段时间再执行.

      add.apply_async((2,3), countdown=5)
      
    • eta : 定义任务的开始时间

      add.apply_async((2,3), eta=now+tiedelta(second=10))
      
    • expires : 设置超时时间.

      add.apply_async((2,3), expires=60)
      
    • retry : 定时如果任务失败后, 是否重试.

      add.apply_async((2,3), retry=False)
      
    • retry_policy : 重试策略.

      • max_retries : 最大重试次数, 默认为 3 次.
      • interval_start : 重试等待的时间间隔秒数, 默认为 0 , 表示直接重试不等待.
      • interval_step : 每次重试让重试间隔增加的秒数, 可以是数字或浮点数, 默认为 0.2
      • interval_max : 重试间隔最大的秒数, 即 通过 interval_step 增大到多少秒之后, 就不在增加了, 可以是数字或者浮点数, 默认为 0.2

    自定义发布者,交换机,路由键, 队列, 优先级,序列方案和压缩方法:

    task.apply_async((2,2), compression='zlib',
        serialize='json',
        queue='priority.high',
        routing_key='web.add',
        priority=0,
        exchange='web_exchange')
    

使用任务调度

from datetime import timedelta
from celery.schedules import crontab

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

# 设置时区
CELERY_TIMEZONE = 'Asia/Shanghai'


# 导入指定的任务模块
CELERY_IMPORT = (
	'celery_app.task1',
	'celery_app.task2',
)

CELERYBEAT_SCHEDULE = {
     
	# 每隔10秒钟执行任务
	'task1':{
     
		'task':'celery_app.task1.add',
		'schedule':timedelta(seconds=10),
		'args':(2,8)
	},

	# 每天的19:28分执行任务
	'task2':{
     
		'task':'celery_app.task2.multiply',
		'schedule':crontab(hour=19,minute=28),
		'args':(4,5)
	}
}

# 使用
# Worker celery worker -A task  -l INFO     启动worker
# Beat: celery beat -A task -l INFO         启动beat
# celery -B -A task worker -l INFO         同时启动beat worker
# celery worker --help 

使用自定义调度类还可以实现动态添加任务. 使用 Django 可以通过 Django-celery 实现在管理后台创建,删除,更新任务, 是因为他使用了自定义的 调度类 djcelery.schedulers.DatabaseScheduler .

任务绑定, 记录日志, 重试

# 修改 tasks.py 文件.
 
from celery.utils.log import get_task_logger
logger = get_task_logger(__name__)
 
@app.task(bind=True)
def div(self, x, y):
    logger.info(('Executing task id {0.id}, args: {0.args!r}'
                 'kwargs: {0.kwargs!r}').format(self.request))
    try:
        result = x/y
    except ZeroDivisionError as e:
        raise self.retry(exc=e, countdown=5, max_retries=3)     # 发生 ZeroDivisionError 错误时, 每 5s 重试一次, 最多重试 3 次.
 
    return result

当使用 bind=True 参数之后, 函数的参数发生变化, 多出了参数 self, 这这相当于把 div 编程了一个已绑定的方法, 通过 self 可以获得任务的上下文.

信号系统

信号可以帮助我们了解任务执行情况, 分析任务运行的瓶颈. Celery 支持 7 种信号类型.

  1. 任务信号

    • before_task_publish : 任务发布前
    • after_task_publish : 任务发布后
    • task_prerun : 任务执行前
    • task_postrun : 任务执行后
    • task_retry : 任务重试时
    • task_success : 任务成功时
    • task_failure : 任务失败时
    • task_revoked : 任务被撤销或终止时
  2. 应用信号

  3. Worker 信号

  4. Beat 信号

  5. Eventlet 信号

  6. 日志信号

  7. 命令信号

不同的信号参数格式不同, 具体格式参见官方文档

# 在执行任务 add 之后, 打印一些信息.
@after_task_publish
def task_send_handler(sender=None, body=None, **kwargs):
    print 'after_task_publish: task_id: {body[id]}; sender: {sender}'.format(body=body, sender=sender)

根据任务状态执行不同操作

# tasks.py
class MyTask(Task):
 
    def on_success(self, retval, task_id, args, kwargs):
        print 'task done: {0}'.format(retval)
        return super(MyTask, self).on_success(retval, task_id, args, kwargs)
 
    def on_failure(self, exc, task_id, args, kwargs, einfo):
        print 'task fail, reason: {0}'.format(exc)
        return super(MyTask, self).on_failure(exc, task_id, args, kwargs, einfo)
 
# 正确函数, 执行 MyTask.on_success() :
@app.task(base=MyTask)
def add(x, y):
    return x + y
 
# 错误函数, 执行 MyTask.on_failure() : 
@app.task  #普通函数装饰为 celery task
def add(x, y):
    raise KeyError
    return x + y

Flower web 监控工具

  • 查看任务历史,任务具体参数,开始时间等信息;
  • 提供图表和统计数据
  • 实现全面的远程控制功能, 包括但不限于 撤销/终止任务, 关闭重启 worker, 查看正在运行任务
  • 提供一个 HTTP API , 方便集成.

Flower 的 supervisor 管理配置文件:

[program:flower]
command=/opt/PyProjects/venv/bin/flower -A celery_worker:celery --broker="redis://localhost:6379/2" --address=0.0.0.0 --port=5555 
directory=/opt/PyProjects/app
autostart=true
autorestart=true
startretries=3 
user=derby
stdout_logfile=/var/logs/%(program_name)s.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=30
stderr_logfile=/var/logs/%(program_name)s-error.log
stderr_logfile_maxbytes=50MB
stderr_logfile_backups=3

你可能感兴趣的:(Python,操作系统OS,Celery,Celery)