celery异步任务队列入门
参考:
Celery入门
任务调度delay&apply_async
celery 简要概述
Celery 中文手册
Celery动态添加定时任务
全网最细之Celery 4.x动态添加定时任务(这个还可以)
win10 Celery异步任务报错: Task handler raised error: ValueError('not enough values to unpack (expected 3, got 0)
celery最佳实践
Flower_0.9.1-Celery 监控工具
Celery使用指南
celery redis版本踩过的坑
celery配置 - Celery官方文档
使用Celery调度任务(下)
将CELERY_ALWAYS_EAGER设置为True会引发错误
celery同步执行任务(在项目中debug任务)
CELERY 常用配置介绍
Celery 信号
前言
在开发工作中,我们很多时候需要对一些任务做异步处理,或者要执行某些定时任务。在蓝鲸python开发框架中,采用了celery这个异步任务队列框架,去调度和执行我们开发的任务。今天来了解一下celery的几个概念、处理流程原理和使用配置。
概念
Celery是一个异步任务的调度工具,是Distributed Task Queue,分布式任务队列,分布式决定了可以有多个worker的存在,队列表示其是异步操作,即存在一个产生任务提出需求的工头,和一群等着被分配工作的码农。
celery可以执行和调度任务,但本身不提供队列服务,所以我们还需要一个消息队列中间件。
来看看celery的几个核心概念:
- task:就是任务,celery调度和执行的对象,有异步任务和周期任务
- broker:中间人,接收生产者发来的消息即task,将任务存入队列,由worker去消费。一般会配置使用redis和rabbitmq作为celery的消息中间件。
- worker:执行任务的单元,它实时监控消息队列,如果有任务就获取任务并执行它。也就是我们任务队列的消费者,worker可以有多个。一个worker只能同时执行一个任务,多个worker可以并行执行,如果worker都满了那剩下的任务就会进入队列排队。
- beat:任务调度器,beat进程会读取配置文件的内容,周期性的将配置中到期需要执行的任务发送给任务队列。django-celery-beat插件本质上是对数据库表变化检查,一旦有数据库表改变,调度器重新读取任务进行调度
- backend:任务执行结果存储,保存任务执行的结果和状态,可以是数据库,或者缓存。

使用
来看看在蓝鲸python3开发框架中怎么使用celery
先找到PROJECT_ROOT/config/default.py
# CELERY 开关,使用时请改为 True,修改项目目录下的 Procfile 文件,添加以下两行命令:
# worker: python manage.py celery worker -l info
# beat: python manage.py celery beat -l info
# 不使用时,请修改为 False,并删除项目目录下的 Procfile 文件中 celery 配置
IS_USE_CELERY = True
# CELERY 并发数,默认为 2,可以通过环境变量或者 Procfile 设置
CELERYD_CONCURRENCY = os.getenv("BK_CELERYD_CONCURRENCY", 8) # noqa
# CELERY 配置,申明任务的文件路径,即包含有 @task 装饰器的函数文件
CELERY_IMPORTS = ("portrait.celery_tasks", "data_sync.celery_tasks", "common.celery_tasks")
"""
以下为框架代码 请勿修改
"""
# celery settings
if IS_USE_CELERY:
INSTALLED_APPS = locals().get("INSTALLED_APPS", [])
import djcelery
INSTALLED_APPS += ("djcelery",)
djcelery.setup_loader()
CELERY_ENABLE_UTC = False
# django_celery_beat调度器
CELERYBEAT_SCHEDULER = "djcelery.schedulers.DatabaseScheduler"
把IS_USE_CELERY打开,设置并发的worker数量,以及从哪些模块中导入celery任务。
这里CELERY_IMPORTS如果不配置,默认导入每个app下面的tasks模块
然后到我们定义的celery模块去写我们的任务,比如common.celery_tasks
@task
def a():
print("aaa")
@periodic_task(run_every=crontab(minute="*/1"))
def b():
print("bbb")
加上了@task、@periodic_task这样装饰器的函数,就是可以被celery调度执行的任务,我们可以使用它的delay()和apply_async()来调度执行任务。@periodic_task装饰的任务还会被beat监听并周期性加入到worker。
delay()与apply_async()
delay与apply_async都是用来做任务调度,但如果查看delay的源码会发现最终还是调用了apply_async,简单来说delay就是apply_async的快捷方式,而apply_async则有很多参数来控制任务调度。
apply_async支持常用参数:
- eta:指定任务执行时间,类型为datetime时间类型;
- countdown:倒计时,单位秒,浮点类型;
- expires:任务过期时间,如果任务在超过过期时间还未开始执行则回收任务,浮点类型(单位秒)获取datetime类型;
- retry:任务执行失败时候是否尝试,布尔类型;
- serializer:序列化方案,支持pickle、json、yaml、msgpack;
- priority:任务优先级,有0~9优先级可设置,int类型;
- retry_policy:任务重试机制,其中包含几个重试参数,类型是dict如下:
- max_retries:最大重试次数
- interval_start:重试等待时间
- interval_step:每次重试叠加时长,假设第一重试等待1s,第二次等待1+n秒
- interval_max:最大等待时间
自定义任务类
我们可以自定义celery的Task和PeriodicTask任务类,重写回调方法加入自己的处理逻辑。
from celery.task.base import PeriodicTask, Task
from blueapps.utils.logger import logger_celery
class CustomTask(Task):
"""自定义Celery任务类"""
abstract = True
pass
class CustomPeriodicTask(PeriodicTask):
"""自定义Celery周期任务类"""
abstract = True
def on_success(self, retval, task_id, args, kwargs):
"""异步任务执行成功时,会执行这个回调方法"""
return super().on_success(retval, task_id, args, kwargs)
def on_failure(self, exc, task_id, args, kwargs, einfo):
"""异步任务执行失败时,会执行这个回调方法"""
logger_celery.exception("task执行失败,name: {}[{}],msg: {}".format(self.name, task_id, exc))
return super().on_failure(exc, task_id, args, kwargs, einfo)
def retry(
self, args=None, kwargs=None, exc=None, throw=True, eta=None, countdown=None, max_retries=None, **options
):
"""异步任务尝试重试时,会执行这个回调方法"""
return super().retry(args, kwargs, exc, throw, eta, countdown, max_retries, **options)
def after_return(self, status, retval, task_id, args, kwargs, einfo):
"""异步任务执行成功,并且return了一些内容,会执行这个回调方法"""
return super().after_return(status, retval, task_id, args, kwargs, einfo)
def update_state(self, task_id=None, state=None, meta=None):
"""可以手动调用这个方法来更新任务状态"""
return super(CustomPeriodicTask, self).update_state(task_id, state, meta)
def send_error_email(self, context, exc, **kwargs):
"""异步任务执行失败时,并且配置了send_error_emails=True时,会执行这个回调方法"""
return super().send_error_email(context, exc, **kwargs)
绑定任务
我们可以给任务绑定自身实例,拿到任务相关的信息
@task(bind=True)
def b(self):
time.sleep(4)
print("bbb", self.request)
这里self就是任务实例b,self.request包含了task、id、args、kwargs、retries、eta、expires等很多属性。
我们可以拿到task_id去获取任务执行状态。
@task(bind=True)
def b(self):
time.sleep(4)
task_id = self.request.id
print("bbb", task_id)
result = self.AsyncResult(task_id)
status = result.status
当然了没有开启结果存储会报错AttributeError("'DisabledBackend' object has no attribute '_get_task_meta_for'",)
AsyncResult对象里面存的就是一个异步的结果,当任务完成时result.ready()为true,然后用result.get()也可以取结果。
>>> b.delay()
<AsyncResult: ae3d31f5-8efe-4d2b-af1c-46a1be83d445>
>>> b.AsyncResult("ae3d31f5-8efe-4d2b-af1c-46a1be83d445")
<AsyncResult: ae3d31f5-8efe-4d2b-af1c-46a1be83d445>
>>> result = b.AsyncResult("ae3d31f5-8efe-4d2b-af1c-46a1be83d445")
>>> result
<AsyncResult: ae3d31f5-8efe-4d2b-af1c-46a1be83d445>
>>> result.ready()
True
>>> result.status
'SUCCESS'
>>> result._get_task_meta()
{'status': 'SUCCESS', 'result': None, 'traceback': None, 'children': []}
celery的工作流
Task.delay() --> apply_async --> send_task --> amqp.create_task_message --> amqp.send_task_message --> result=AsyncResult(task_id)返回result
delay之后调用实际上是apply_async之后 调用的send_task之后开始创建任务,发送任务,然后生成一个异步对象,把这个结果返回。
日志打印
在celery任务中,我们使用print()打印的信息级别是warning,建议使用logging明确日志级别打印。打印的日志正常是在worker中,beat只与周期任务的调度有关。
celery命令
在win10下需要指定协程类型eventlet,否则可能会报错
-P/--pool是POOL的配置,默认是prefork(并发),solo是串行,并发可以有prefork、gevent、eventlet,
-l日志级别,-f日志文件路径,在linux上可以用-B参数同步启动celery beat
python manage.py celery worker -l INFO -P eventlet
python manage.py celery beat -l INFO
动态添加定时任务
PeriodicTask
此模型定义要运行的单个周期性任务。
- 必须为任务指定一种Schedule,即clocked, interval, crontab, solar四个字段必须填写一个,且只能填写一个
- name字段给任务命名,它是unique的
- task字段指定运行的Celery任务,如"proj.tasks.test_task"
- one_off:默认值为False,如果one_off=True,任务被运行一次后enabled字段将被置为False,即任务只会运行一次
- args:传递给任务的参数,是一个json字符串,如 ["arg1", "arg2"]
- expires:过期时间,过期的任务将不再会被驱动触发
使用ClockedSchedule:在特定的时间执行任务
def test_clock():
clock = ClockedSchedule.objects.create(clocked_time=datetime.now() + timedelta(seconds=10))
PeriodicTask.objects.create(
name="%s" % str(datetime.now()),
task="project_celery.celery_app.test_task",
clocked=clock,
# 如果使用ClockedSchedule,则one_off必须为True
one_off=True
)
使用IntervalSchedule:以特定间隔运行的Schedule
用IntervalSchedule能够实现与ClockedSchedule同样的功能:计算目标时间与当前时间的时间差,令此时间差作为IntervalSchedule的周期,并且将任务的one_off参数置为True
def time_diff(target_time):
diff = target_time - datetime.now()
return int(diff.total_seconds())
def test_interval():
seconds = time_diff(datetime.strptime("2020-3-19 15:39:00", "%Y-%m-%d %H:%M:%S"))
schedule = IntervalSchedule.objects.create(every=seconds, period=IntervalSchedule.SECONDS)
PeriodicTask.objects.create(
name="%s" % str(datetime.now()),
task="project_celery.celery_app.test_task",
interval=schedule,
one_off=True
)
使用CrontabSchedule
使用CrontabSchedule一定要注意将时区设置为当前地区时区
model参数与crontab表达式的对应关系:
minite, hour, day_of_week, day_of_month, month_of_year
全部默认为"*"
def test_crontab():
# 表示 * * * * * ,即每隔一分钟触发一次
schedule = CrontabSchedule.objects.create(timezone='Asia/Shanghai')
PeriodicTask.objects.create(
name="%s" % str(datetime.now()),
task="project_celery.celery_app.test_task",
crontab=schedule,
one_off=True
)
flower可视化工具
pip install flower==0.9.1
python manage.py flower
celery配置
# 时区
CELERY_TIMEZONE = "Asia/Shanghai"
# 消息中间件
BROKER_URL = "redis://:password@localhost:6379/0"
# 使用redis存储任务执行结果,默认不使用
# 遇到redis.exceptions.ResponseError时降低版本pip install --upgrade redis==2.10.6
CELERY_RESULT_BACKEND = "redis://:123456@localhost:6379/1"
# 任务序列化和反序列化为json
CELERY_TASK_SERIALIZER = "json"
# 存储结果序列化为json
CELERY_RESULT_SERIALIZER = "json"
# CELERY 并发数,worker数量
CELERYD_CONCURRENCY = 2
# 禁用时区设置
CELERY_ENABLE_UTC = True
# 限制任务的执行频率
# 下面这个就是限制tasks模块下的add函数,每秒钟只能执行10次
CELERY_ANNOTATIONS = {'tasks.add':{'rate_limit':'10/s'}}
# 或者限制所有的任务的刷新频率
CELERY_ANNOTATIONS = {'*':{'rate_limit':'10/s'}}
# 也可以设置如果任务执行失败后调用的函数
def my_on_failure(self,exc,task_id,args,kwargs,einfo):
print('task failed')
CELERY_ANNOTATIONS = {'*':{'on_failure':my_on_failure}}
# 并发的worker数量,也是命令行-c指定的数目
# 事实上并不是worker数量越多越好,保证任务不堆积,加上一些新增任务的预留就可以了
CELERYD_CONCURRENCY = 4
# celery worker每次去mq取任务的数量,默认值就是4
CELERYD_PREFETCH_MULTIPLIER = 4
# 每个worker执行了多少次任务后就会死掉,建议数量大一些
CELERYD_MAX_TASKS_PER_CHILD = 200
# celery任务执行结果的超时时间
CELERY_TASK_RESULT_EXPIRES = 1200
# 单个任务的运行时间限制,超过会被杀死
CELERYD_TASK_TIME_LIMIT = 60
CELERY_DISABLE_RATE_LIMITS = True
# 任务本地阻塞执行
CELERY_ALWAYS_EAGER = False
CELERY_ALWAYS_EAGER
开启后所有任务都会本地阻塞执行,apply_async()和delay()都只会在执行完返回结果,与普通方法一样执行而没有异步效果。
不会送到队列和worker,一切由beat调度执行,是串行的。
因为不经过worker和中间件,所以只在beat打印日志,并且对于异常中断不会打印任何信息,worker和中间件看不到任何记录。
好处是你可以在不启动worker或消息中间件的情况下执行你的任务或者debug你的任务。
CELERY_ALWAYS_EAGER = False
以下代码在CELERY_ALWAYS_EAGER开关下分别放到celery中执行,就可以看到明显的串行和并行区别。
@periodic_task(run_every=crontab(minute="*/1"), base=CustomPeriodicTask)
def a():
time.sleep(30)
print("aaa")
raise Exception("hahah")
@periodic_task(run_every=crontab(minute="*/1"), base=CustomPeriodicTask)
def b(self):
time.sleep(4)
print("bbb")
celery异步任务队列入门的更多相关文章
- [Flask]celery异步任务队列的使用
Celery异步任务队列 目录结构树: 配置文件config.py: # 设置中间人地址 broker_url = 'redis://127.0.0.1:6379/1' 主main.py: impor ...
- Django使用Celery异步任务队列
1 Celery简介 Celery是异步任务队列,可以独立于主进程运行,在主进程退出后,也不影响队列中的任务执行. 任务执行异常退出,重新启动后,会继续执行队列中的其他任务,同时可以缓存停止期间接收 ...
- Celery 分布式任务队列入门
一.Celery介绍和基本使用 Celery 是一个 基于python开发的分布式异步消息任务队列,通过它可以轻松的实现任务的异步处理, 如果你的业务场景中需要用到异步任务,就可以考虑使用celery ...
- Celery异步任务队列/周期任务+ RabbitMQ + Django
一.Celery介绍和基本使用 Celery 是一个 基于python开发的分布式异步消息任务队列,通过它可以轻松的实现任务的异步处理, 如果你的业务场景中需要用到异步任务,就可以考虑使用celer ...
- Celery 分布式任务队列快速入门
Celery 分布式任务队列快速入门 本节内容 Celery介绍和基本使用 在项目中如何使用celery 启用多个workers Celery 定时任务 与django结合 通过django配置cel ...
- Celery 分布式任务队列快速入门 以及在Django中动态添加定时任务
Celery 分布式任务队列快速入门 以及在Django中动态添加定时任务 转自 金角大王 http://www.cnblogs.com/alex3714/articles/6351797.html ...
- 【转】Celery 分布式任务队列快速入门
Celery 分布式任务队列快速入门 本节内容 Celery介绍和基本使用 在项目中如何使用celery 启用多个workers Celery 分布式 Celery 定时任务 与django结合 通过 ...
- 异步任务队列Celery在Django中的使用
前段时间在Django Web平台开发中,碰到一些请求执行的任务时间较长(几分钟),为了加快用户的响应时间,因此决定采用异步任务的方式在后台执行这些任务.在同事的指引下接触了Celery这个异步任务队 ...
- celery异步消息处理框架
Celery 1.什么是Clelery Celery是一个简单.灵活且可靠的,处理大量消息的分布式系统 专注于实时处理的异步任务队列 同时也支持任务调度 Celery架构 Celery的架构由三部分组 ...
- day21 git & github + Celery 分布式任务队列
参考博客: git & github 快速入门http://www.cnblogs.com/alex3714/articles/5930846.html git@github.com:liyo ...
随机推荐
- rxjs笔记(未完成)
首先是 Observable 和promise的区别, 1返回值个数,Observable 可以返回0到无数个值. 2.Promise主动推送,控制着"值"何时被 "推送 ...
- mongodb对Obeject对象进行增删改操作
1.插入某个对象,如User对象 mongoTemplate.insert(user, "users");//第一个参数是实体类对象User, 第二个参数是mongodb对应的集合 ...
- maven 引入了jar包,但却不能使用jar包里类
无报错,但是就是 无法 使用 lombok 的类. 发现classpath 里面也的确没有lombok jar包. 最后把json 的 version 属性加上 就正常了. 所以 结论: 不加vers ...
- python装饰器中高级用法(函数加参)
在上一章我们说到装饰器的原则和基本用法,下面来补充一下:如果函数加参,装饰器该如何变化 1,还是用上一章的源代码 2,给test2加个参数name 报错了,本来给test2加一个name参数,为了实现 ...
- Office & WPS 基础篇
基础知识 安装office 推荐使用Microsoft Office,原因不详,不做讨论 使用Office tool plus安装,不要三年前版本,一般三年一代,工具自己找,版本文件自己找. 具体选项 ...
- 397. 整数替换 (Medium
问题描述 397. 整数替换 (Medium) 给定一个正整数 n ,你可以做如下操作: 如果 n 是偶数,则用 n / 2 替换 n. 如果 n 是奇数,则可以用 n + 1 或 n - 1 替换 ...
- Resport 四则运算
使用FormatNumber 即可 [FormatNumber([Total2]/[Total1]*100)]%
- IntelliJ IDEA 工具识别不了 过大Java文件 引用识别的不了的文件报错
之前出现过proto 生成的文件过大 idea 识别不了 引用消息报错. 解决方案 1.找到IntelliJ IDEA 桌面快捷键图标 右键属性 2.打开文件位置,找到所在目录 3.找到idea.pr ...
- js - script标签的for属性和event属性
js - script标签的for属性和event属性 <script language="javascript" for="window" event= ...
- nginx服务器有什么作用?什么叫反向代理?为什么要使用反向代理?
1 背景介绍 1.1 Nginx是什么? Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器.其特点是占有内存少,并发能力强,事实上nginx的并发能力确 ...