分布式任务队列 Celery —— 深入 Task
目录
前文列表
分布式任务队列 Celery
分布式任务队列 Celery —— 详解工作流
分布式任务队列 Celery —— 应用基础
前言
紧接前文,继续深入了解 Celery Tasks。示例代码依旧在前文的基础上进行修改。
Tasks 是 Celery 的基石,原型类为 celery.app.task:Task,它提供了两个核心功能:
- 将任务消息发送到队列
- 声明 Worker 接收到消息后需要执行的具体函数
Task 的实例化
使用装饰器 app.task 来装饰一个普通函数,就可以轻松创建出一个任务函数。
from proj.celery import app
@app.task
def add(x, y):
return x + y
值得注意的是,任务函数本质上已经不再是一个普通函数,而是一个 celery.app.task:Task 实例对象。
>>> from proj.task.tasks import add
>>> dir(add)
['AsyncResult', 'MaxRetriesExceededError', ..., 'apply_async', 'delay', u'name', 'on_bound', 'on_failure', 'on_retry', 'on_success', 'request', 'retry', 'subtask', ...]
所以任务函数才可以调用 delay/apply_async 等属于 Task 的实例属性和方法。
>>> add.apply_async
<bound method add.apply_async of <@task: proj.task.tasks.add of proj at 0x7fedb363a790>>
>>> add.delay
<bound method add.delay of <@task: proj.task.tasks.add of proj at 0x7fedb363a790>>
这是一个非常重要的认识「app.task 的 “装饰” 动作,其实是 Task 的实例化过程」,而装饰器的参数就是 Task 初始化的参数。
官方文档(http://docs.celeryproject.org/en/latest/userguide/tasks.html#list-of-options)提供了完整的 app.task 装饰器参数列表。
除此之外,还需要注意「多装饰器顺序」的坑,app.task 应该始终放到最后使用,才能保证其稳定有效。
app.task
@decorator2
@decorator1
def add(x, y):
return x + y
任务的名字
每个任务函数都具有唯一的名字,这个名字被包含在任务消息中,Worker 通过该名字来找到具体执行的任务函数。默认的,Task 会启用自动命名,将函数的全路径名作为任务名。
>>> add.name
u'proj.task.tasks.add'
当然了,也可以通过指定装饰器参数 name 来指定任务名。
@app.task(name='new_name')
def add(x, y):
return x + y
>>> from proj.task.tasks import add
>>> add.name
'new_name'
但为了避免命名冲突的问题,一般不建议这么做,除非你很清楚自己的做什么。
任务的绑定
既然任务函数本质是一个 Task 实例对象,那么当然也可以应用 self 绑定特性。
# 启用绑定:
@app.task(bind=True)
def add(self, x, y):
print("self: ", self)
return x + y
>>> add.delay(1, 2)
<AsyncResult: 1982dc85-694b-4ceb-849b-5f69e40b4fe9>
绑定对象 self 十分重要,Task 的很多高级功能都是依靠它作为载体来调用的。例如:任务重试功能,请求上下文功能。
任务的重试
任务重试功能的实现为 Task.retry,它将任务消息重新发送到同一个的队列中,以此来重启任务。
@app.task(bind=True, max_retries=3)
def send_twitter_status(self, oauth, tweet):
try:
twitter = Twitter(oauth)
twitter.update_status(tweet)
except (Twitter.FailWhaleError, Twitter.LoginError) as exc:
raise self.retry(exc=exc)
- max_retries 指定了最大的重试次数
- exc 指定将异常信息输出到日志,需要开启 result backend。
如果你仅希望触发特定异常时才进行重试,可以应用 Task 的「Automatic retry for known exceptions」特性。
# 只有在触发 FailWhaleError 异常时,才会重试任务,且最多重试 5 次。
@app.task(autoretry_for=(FailWhaleError,), retry_kwargs={'max_retries': 5})
def refresh_timeline(user):
return twitter.refresh_timeline(user)
任务的请求上下文
在 Celery 请求 Worker 执行任务函数时,提供了请求的上下文,这是为了让任务函数在执行过程中能够访问上下文所包含的任务状态和信息。
@app.task(bind=True)
def dump_context(self, x, y):
print('Executing task id {0.id}, args: {0.args!r} kwargs: {0.kwargs!r}'.format(
self.request))
>>> from proj.task.tasks import dump_context
>>> dump_context.delay(1, 2)
<AsyncResult: 00bc9f96-98df-4bca-a4a3-4774c535a44c>
捕获请求上下文中的有用信息,有利于我们去分辨和调查一个任务的执行状况。
完整的上下文属性列表,可以查阅官方文档(http://docs.celeryproject.org/en/latest/userguide/tasks.html#task-request)。
任务的继承
使用 app.task 装饰器默认会实例化原生的 Task 类,这虽然能满足大部分的应用场景需求,但并非全部。所以 Celery 允许我们通过继承 Task 类,来衍生出特异化的基类。这一特性在复杂的应用场景中将会十分有效。
- 重新指定适用于的所有任务的默认基类
def make_app(context):
app = Celery('proj')
app.config_from_object('proj.celeryconfig')
default_exchange = Exchange('default', type='direct')
web_exchange = Exchange('task', type='direct')
app.conf.task_default_queue = 'default'
app.conf.task_default_exchange = 'default'
app.conf.task_default_routing_key = 'default'
app.conf.task_queues = (
Queue('default', default_exchange, routing_key='default'),
Queue('high_queue', web_exchange, routing_key='hign_task'),
Queue('low_queue', web_exchange, routing_key='low_task'),
)
app.conf.timezone = 'Asia/Shanghai'
app.conf.beat_schedule = {
'periodic_task_add': {
'task': 'proj.task.tasks.add',
'schedule': crontab(minute='*/1'),
'args': (2, 2)
},
}
TaskBase = app.Task
class ContextTask(TaskBase):
abstract = True
context = ctx
def __call__(self, *args, **kwargs):
"""Will be execute when create the instance object of ContextTesk.
"""
LOG.info(_LI("Invoked celery task starting: %(name)s[%(id)s]"),
{'name': self.name, 'id': self.request.id})
return super(ContextTask, self).__call__(*args, **kwargs)
# 任务执行成功前做什么
def on_success(self, retval, task_id, args, kwargs):
"""Invoked after the task is successfully execute.
"""
LOG.info(_LI("Task %(id)s success: [%(ret)s]."),
{'id': task_id, 'ret': retval})
return super(ContextTask, self).on_success(retval, task_id,
args, kwargs)
# 任务执行失败后做什么
def on_failure(self, exc, task_id, args, kwargs, einfo):
"""Invoked after the task failed to execute.
"""
msg = _LE("Task [%(id)s] failed:\n"
"args : %(args)s\n"
"kwargs : %(kw)s\n"
"detail :%(err)s") % {
'id': task_id, 'args': args,
'kw': kwargs, 'err': six.text_type(exc)}
LOG.exception(msg)
return super(ContextTask, self).on_failure(exc, task_id, args,
kwargs, einfo)
# 重新赋予默认基类
app.Task = ContextTask
return app
- 为不同类型的任务继承出具有偏向属性的基类
Import celery
class JSONTask(celery.Task):
serializer = 'json'
def on_failure(self, exc, task_id, args, kwargs, einfo):
print('{0!r} failed: {1!r}'.format(task_id, exc))
class XMLTask(celery.Task):
serializer = 'xml'
def on_failure(self, exc, task_id, args, kwargs, einfo):
print('{0!r} failed: {1!r}'.format(task_id, exc))
# 指定不同的基类
@task(base=JSONTask)
def add_json(x, y):
raise KeyError()
# 指定不同的基类
@task(base=XMLTask)
def add_xml(x, y):
raise KeyError()
分布式任务队列 Celery —— 深入 Task的更多相关文章
- [源码解析] 并行分布式任务队列 Celery 之 Task是什么
[源码解析] 并行分布式任务队列 Celery 之 Task是什么 目录 [源码解析] 并行分布式任务队列 Celery 之 Task是什么 0x00 摘要 0x01 思考出发点 0x02 示例代码 ...
- [源码分析] 分布式任务队列 Celery 之 发送Task & AMQP
[源码分析] 分布式任务队列 Celery 之 发送Task & AMQP 目录 [源码分析] 分布式任务队列 Celery 之 发送Task & AMQP 0x00 摘要 0x01 ...
- [源码解析] 并行分布式任务队列 Celery 之 消费动态流程
[源码解析] 并行分布式任务队列 Celery 之 消费动态流程 目录 [源码解析] 并行分布式任务队列 Celery 之 消费动态流程 0x00 摘要 0x01 来由 0x02 逻辑 in komb ...
- [源码分析] 并行分布式任务队列 Celery 之 Timer & Heartbeat
[源码分析] 并行分布式任务队列 Celery 之 Timer & Heartbeat 目录 [源码分析] 并行分布式任务队列 Celery 之 Timer & Heartbeat 0 ...
- 分布式任务队列 Celery —— Task对象
转载至 JmilkFan_范桂飓:http://blog.csdn.net/jmilk 目录 目录 前文列表 前言 Task 的实例化 任务的名字 任务的绑定 任务的重试 任务的请求上下文 任务的继 ...
- 分布式任务队列 Celery —— 应用基础
目录 目录 前文列表 前言 Celery 的周期定时任务 Celery 的同步调用 Celery 结果储存 Celery 的监控 Celery 的调试 前文列表 分布式任务队列 Celery 分布式任 ...
- 分布式任务队列 Celery —— 详解工作流
目录 目录 前文列表 前言 任务签名 signature 偏函数 回调函数 Celery 工作流 group 任务组 chain 任务链 chord 复合任务 chunks 任务块 mapstarma ...
- 分布式任务队列 Celery
目录 目录 前言 简介 Celery 的应用场景 架构组成 Celery 应用基础 前言 分布式任务队列 Celery,Python 开发者必备技能,结合之前的 RabbitMQ 系列,深入梳理一下 ...
- [源码解析] 分布式任务队列 Celery 之启动 Consumer
[源码解析] 分布式任务队列 Celery 之启动 Consumer 目录 [源码解析] 分布式任务队列 Celery 之启动 Consumer 0x00 摘要 0x01 综述 1.1 kombu.c ...
随机推荐
- JS 一位数左边补零
- 2019-11-29-VisualStudio-解决方案筛选器-slnf-文件
title author date CreateTime categories VisualStudio 解决方案筛选器 slnf 文件 lindexi 2019-11-29 08:41:13 +08 ...
- php实用小技巧【持续更新】
这是本人开始做项目的时候遇到过的问题还有解决方法 1.eval函数 能把字符串转换成可执行的php代码,如果字符串不是可执行的php代码的话,需要在前面加上@,屏蔽notice 2.array_mer ...
- 实践周java基础软件开发app之五子棋
五子棋人机对战实践项目 总的任务和目标 完成一个人机对战的五子棋项目,基本效果如下: 第一部分 Java绘图原理 1. 基本概念 像素,坐标 第二部分 绘制棋盘 1. 基本思路 在一个JPan ...
- inux下:热插拔和模块是什么
一.何为模块? 文件系统.设备驱动程序.网络协议都可以理解为模块.模块本质也是普通的软件系统. 二.热插拔 硬件层面:只在不断电.不关闭系统的情况下增加或者删除对应部件,比如电源.硬盘.一些高端设备硬 ...
- zencart后台订单详细页显示产品图片和链接
方法一: 找到admin/order.php 大约491行 for ($i=0, $n=sizeof($order->products); $i<$n; $i++) { 与 if (iss ...
- MySQL 数据库的创建、修改、删除、跳转
一. 创建数据库 []代表可选项. IF NOT EXISTS:在创建数据库之前进行判断,只有该数据库目前尚不存在时才能执行操作. <数据库名>:创建数据库的名称.MySQL 的数据存储区 ...
- 安装php多进程模块pcntl
在使用函数pcntl_fork()时报错 Fatal error: Uncaught Error: Call to undefined function pcntl_fork()....,原因是没有 ...
- linux常见的操作指令
一:非正确退出程序,再次启动显示端口被占用 netstat -ntulp n --- 显示ip代替网络接口信息,显示出网络连接情况 t --- 显示TCP协议的链接状况 u --- 显示UDP ...
- JS语法基础-基本使用及数据类型分类
JS基础 --------------- 什么是JS? ------------------ JS的全称是Javascript. ----------------------------- 老婆和老婆 ...