参考:

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

此模型定义要运行的单个周期性任务。

  1. 必须为任务指定一种Schedule,即clocked, interval, crontab, solar四个字段必须填写一个,且只能填写一个
  2. name字段给任务命名,它是unique的
  3. task字段指定运行的Celery任务,如"proj.tasks.test_task"
  4. one_off:默认值为False,如果one_off=True,任务被运行一次后enabled字段将被置为False,即任务只会运行一次
  5. args:传递给任务的参数,是一个json字符串,如 ["arg1", "arg2"]
  6. 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 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异步任务队列入门的更多相关文章

  1. [Flask]celery异步任务队列的使用

    Celery异步任务队列 目录结构树: 配置文件config.py: # 设置中间人地址 broker_url = 'redis://127.0.0.1:6379/1' 主main.py: impor ...

  2. Django使用Celery异步任务队列

    1  Celery简介 Celery是异步任务队列,可以独立于主进程运行,在主进程退出后,也不影响队列中的任务执行. 任务执行异常退出,重新启动后,会继续执行队列中的其他任务,同时可以缓存停止期间接收 ...

  3. Celery 分布式任务队列入门

    一.Celery介绍和基本使用 Celery 是一个 基于python开发的分布式异步消息任务队列,通过它可以轻松的实现任务的异步处理, 如果你的业务场景中需要用到异步任务,就可以考虑使用celery ...

  4. Celery异步任务队列/周期任务+ RabbitMQ + Django

    一.Celery介绍和基本使用  Celery 是一个 基于python开发的分布式异步消息任务队列,通过它可以轻松的实现任务的异步处理, 如果你的业务场景中需要用到异步任务,就可以考虑使用celer ...

  5. Celery 分布式任务队列快速入门

    Celery 分布式任务队列快速入门 本节内容 Celery介绍和基本使用 在项目中如何使用celery 启用多个workers Celery 定时任务 与django结合 通过django配置cel ...

  6. Celery 分布式任务队列快速入门 以及在Django中动态添加定时任务

    Celery 分布式任务队列快速入门 以及在Django中动态添加定时任务 转自 金角大王 http://www.cnblogs.com/alex3714/articles/6351797.html ...

  7. 【转】Celery 分布式任务队列快速入门

    Celery 分布式任务队列快速入门 本节内容 Celery介绍和基本使用 在项目中如何使用celery 启用多个workers Celery 分布式 Celery 定时任务 与django结合 通过 ...

  8. 异步任务队列Celery在Django中的使用

    前段时间在Django Web平台开发中,碰到一些请求执行的任务时间较长(几分钟),为了加快用户的响应时间,因此决定采用异步任务的方式在后台执行这些任务.在同事的指引下接触了Celery这个异步任务队 ...

  9. celery异步消息处理框架

    Celery 1.什么是Clelery Celery是一个简单.灵活且可靠的,处理大量消息的分布式系统 专注于实时处理的异步任务队列 同时也支持任务调度 Celery架构 Celery的架构由三部分组 ...

  10. day21 git & github + Celery 分布式任务队列

    参考博客: git & github 快速入门http://www.cnblogs.com/alex3714/articles/5930846.html git@github.com:liyo ...

随机推荐

  1. rxjs笔记(未完成)

    首先是 Observable 和promise的区别, 1返回值个数,Observable 可以返回0到无数个值. 2.Promise主动推送,控制着"值"何时被 "推送 ...

  2. mongodb对Obeject对象进行增删改操作

    1.插入某个对象,如User对象 mongoTemplate.insert(user, "users");//第一个参数是实体类对象User, 第二个参数是mongodb对应的集合 ...

  3. maven 引入了jar包,但却不能使用jar包里类

    无报错,但是就是 无法 使用 lombok 的类. 发现classpath 里面也的确没有lombok jar包. 最后把json 的 version 属性加上 就正常了. 所以 结论: 不加vers ...

  4. python装饰器中高级用法(函数加参)

    在上一章我们说到装饰器的原则和基本用法,下面来补充一下:如果函数加参,装饰器该如何变化 1,还是用上一章的源代码 2,给test2加个参数name 报错了,本来给test2加一个name参数,为了实现 ...

  5. Office & WPS 基础篇

    基础知识 安装office 推荐使用Microsoft Office,原因不详,不做讨论 使用Office tool plus安装,不要三年前版本,一般三年一代,工具自己找,版本文件自己找. 具体选项 ...

  6. 397. 整数替换 (Medium

    问题描述 397. 整数替换 (Medium) 给定一个正整数 n ,你可以做如下操作: 如果 n 是偶数,则用 n / 2 替换 n. 如果 n 是奇数,则可以用 n + 1 或 n - 1 替换 ...

  7. Resport 四则运算

    使用FormatNumber 即可 [FormatNumber([Total2]/[Total1]*100)]%

  8. IntelliJ IDEA 工具识别不了 过大Java文件 引用识别的不了的文件报错

    之前出现过proto 生成的文件过大 idea 识别不了 引用消息报错. 解决方案 1.找到IntelliJ IDEA 桌面快捷键图标 右键属性 2.打开文件位置,找到所在目录 3.找到idea.pr ...

  9. js - script标签的for属性和event属性

    js - script标签的for属性和event属性 <script language="javascript" for="window" event= ...

  10. nginx服务器有什么作用?什么叫反向代理?为什么要使用反向代理?

    1 背景介绍 1.1 Nginx是什么? Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器.其特点是占有内存少,并发能力强,事实上nginx的并发能力确 ...