本文首发于公众号:Hunter后端

原文链接:celery笔记三之task和task的调用

这一篇笔记介绍 task 和 task 的调用。

以下是本篇笔记目录:

  1. 基础的 task 定义方式
  2. 日志处理
  3. 任务重试
  4. 忽略任务运行结果
  5. task 的调用

1、基础的 task 定义方式

前面两篇笔记中介绍了最简单的定义方式,使用 @app.task 作为装饰器:

@app.task
def add(x, y):
return x + y

如果是在 Django 系统中使用 celery,需要定义一个延时任务或者周期定时任务,可以使用 @shared_task 来修饰

from celery import shared_task

@shared_task
def add(x, y):
return x + y

在 Django 系统中使用 celery 的方式会在接下来的几篇笔记中介绍道。

多个装饰器

如果是 celery 的任务和其他装饰器一起联用,记得将 celery 的装饰器放在最后使用,也就是列表的最前面:

@app.task
@decorator1
@decorator2
def add(x, y):
return x + y

task名称

每个 task 都有一个唯一的名称用来标识这个 task,如果我们在定义的时候不指定,系统会为我们默认一个名称,这些名称会在 celery 的 worker 启动的时候被系统扫描然后输出一个列表展示。

还是上一篇笔记中我们定义的两个 task,我们给其中一个指定 name:

#tasks1.py
from .celery import app @app.task(name="tasks1.add")
def add(x, y):
return x + y

可以观察在 celery 的 worker 启动的时候,会有一个输出:

[tasks]
. proj.tasks2.mul
. tasks1.add

可以看到这个地方,系统就会使用我们定义的 name 了。

2、日志处理

我们可以在启动 worker 的时候指定日志的输出,定义格式如下:

celery -A proj worker -l INFO --logfile=/Users/hunter/python/celery_log/celery.log

在 task 中的定义可以使用 celery 中方法:

from celery.utils.log import get_task_logger

logger = get_task_logger(__name__)

也可以直接使用 logging 模块:

import logging

logger1 = logging.getLogger(__name__)

直接在 task 中输出:

@app.task(name="tasks1.add")
def add(x, y):
logger.info("this is from logger")
return x + y

然后在 worker 启动时指定的日志文件就会有我们打印出的日志内容:

[2022-07-24 16:28:33,210: INFO/ForkPoolWorker-7] tasks1.add[4db4b0fc-c6ca-472a-8847-ae42e0a7959a]: this is from logger
[2022-07-24 16:28:33,224: INFO/ForkPoolWorker-7] Task tasks1.add[4db4b0fc-c6ca-472a-8847-ae42e0a7959a] succeeded in 0.016244667931459844s: 3

3、任务重试

对于一个 task,我们可以对其设置 retry 参数来指定其在任务执行失败后会重试几次,以及隔多长时间重试。

比如对于下面的 div() 函数,我们来输入除数为 0 的情况查看重试的功能。

当然,这里我们是故意输入参数错误,在实际的项目中可能会是其他的原因造成任务失败,比如数据库连接失败等

任务重试的参数也都在 @app.task() 中定义:

# tasks1.py

@app.task(autoretry_for=(Exception, ),  default_retry_delay=10, retry_kwargs={'max_retries': 5})
def div(x, y):
return x / y

在这里,autoretry_for 表示的是某种报错情况下重试,我们定义的 Exception 表示任何错误都重试。

如果只是想在某种特定的 exception 情况下重试,将那种 exception 的值替换 Exception 即可。

default_retry_delay 表示重试间隔时长,默认值是 3 * 60s,即三分钟,是以秒为单位,这里我们设置的是 10s。

retry_kwargs 是一个 dict,其中有一个 max_retries 参数,表示的是最大重试次数,我们定为 5

然后可以尝试调用这个延时任务:

from proj.tasks1 import div
div.delay(1, 0)

然后可以看到在日志文件会有如下输出:

[2022-07-24 16:59:35,653: INFO/ForkPoolWorker-7] Task proj.tasks1.div[1f65c410-1b2a-4127-9d83-a84b1ad9dd2c] retry: Retry in 10s: ZeroDivisionError('division by zero',)

且每隔 10s 执行一次,一共执行 5 次,5次之后还是不成功则会报错。

retry_backoff 和 retry_backoff_max

还有一个 retry_backoff 和 retry_backoff_max 参数,这两个参数是用于这种情况:如果你的 task 依赖另一个 service 服务,比如会调用其他系统的 API,然后这两个参数可以用于避免请求过多的占用服务。

retry_backoff 参数可以设置成一个 布尔型数据,为 True 的话,自动重试的时间间隔会成倍的增长

第一次重试是 1 s后

第二次是 2s 后

第三次是 4s 后

第四次是 8s 后

...

如果 retry_backoff 参数是一个数字,比如是 3,那么后续的间隔时间则是 3 的倍数增长

第一次重试 3s 后

第二次是 6s 后

第三次是 12s 后

第四次是 24s 后

retry_backoff_max 是重试的最大的间隔时间,比如重试次数设置的很大,retry_backoff 的间隔时间重复达到了这个值之后就不再增大了。

这个值默认是 600s,也就是 10分钟。

我们看一下下面这个例子:

# tasks1.py

@app.task(autoretry_for=(Exception, ), retry_backoff=2, retry_backoff_max=40, retry_kwargs={'max_retries': 8})
def div(x, y):
return x / y

关于重试的机制,理论上应该是按照我们前面列出来的重试时间间隔进行重试,但是如果我们这样直接运行 div.delay(),得出的间隔时间是不定的,是在 0 到 最大值之间得出的一个随机值。

这样产生的原因是因为还有一个 retry_jitter 参数,这个参数默认是 True,所以时间间隔会是一个随机值。

如果需要任务延时的间隔值是按照 retry_backoff 和 retry_backoff_max 两个设定值来运行,那么则需要将 retry_jitter 值设为 False。

# tasks1.py

@app.task(autoretry_for=(Exception, ), retry_backoff=2, retry_backoff_max=40, retry_jitter=False, retry_kwargs={'max_retries': 8})
def div(x, y):
return x / y

然后运行 div 的延时任务,就可以看到延时任务按照规律的间隔时间重试了,以下是日志:

[2022-07-24 19:00:38,588: INFO/ForkPoolWorker-7] Task proj.tasks1.div[7e689dcf-8069-4f17-8815-fe58f9800fc0] retry: Retry in 2s: ZeroDivisionError('division by zero',)
[2022-07-24 19:00:40,662: INFO/MainProcess] Task proj.tasks1.div[7e689dcf-8069-4f17-8815-fe58f9800fc0] received
[2022-07-24 19:00:40,664: INFO/ForkPoolWorker-7] Task proj.tasks1.div[7e689dcf-8069-4f17-8815-fe58f9800fc0] retry: Retry in 4s: ZeroDivisionError('division by zero',)
[2022-07-24 19:00:44,744: INFO/MainProcess] Task proj.tasks1.div[7e689dcf-8069-4f17-8815-fe58f9800fc0] received
[2022-07-24 19:00:44,746: INFO/ForkPoolWorker-7] Task proj.tasks1.div[7e689dcf-8069-4f17-8815-fe58f9800fc0] retry: Retry in 8s: ZeroDivisionError('division by zero',)
[2022-07-24 19:00:52,870: INFO/MainProcess] Task proj.tasks1.div[7e689dcf-8069-4f17-8815-fe58f9800fc0] received
[2022-07-24 19:00:52,872: INFO/ForkPoolWorker-7] Task proj.tasks1.div[7e689dcf-8069-4f17-8815-fe58f9800fc0] retry: Retry in 16s: ZeroDivisionError('division by zero',)
[2022-07-24 19:01:09,338: INFO/MainProcess] Task proj.tasks1.div[7e689dcf-8069-4f17-8815-fe58f9800fc0] received
[2022-07-24 19:01:09,340: INFO/ForkPoolWorker-7] Task proj.tasks1.div[7e689dcf-8069-4f17-8815-fe58f9800fc0] retry: Retry in 32s: ZeroDivisionError('division by zero',)
[2022-07-24 19:01:41,843: INFO/MainProcess] Task proj.tasks1.div[7e689dcf-8069-4f17-8815-fe58f9800fc0] received
[2022-07-24 19:01:41,845: INFO/ForkPoolWorker-7] Task proj.tasks1.div[7e689dcf-8069-4f17-8815-fe58f9800fc0] retry: Retry in 40s: ZeroDivisionError('division by zero',)
[2022-07-24 19:02:21,923: INFO/MainProcess] Task proj.tasks1.div[7e689dcf-8069-4f17-8815-fe58f9800fc0] received
[2022-07-24 19:02:21,925: INFO/ForkPoolWorker-7] Task proj.tasks1.div[7e689dcf-8069-4f17-8815-fe58f9800fc0] retry: Retry in 40s: ZeroDivisionError('division by zero',)
[2022-07-24 19:03:02,001: INFO/MainProcess] Task proj.tasks1.div[7e689dcf-8069-4f17-8815-fe58f9800fc0] received
[2022-07-24 19:03:02,003: INFO/ForkPoolWorker-7] Task proj.tasks1.div[7e689dcf-8069-4f17-8815-fe58f9800fc0] retry: Retry in 40s: ZeroDivisionError('division by zero',)

因为我们设置的重试间隔时间最大为 40s,所以这个地方延时间隔时间到了 40 之后,就不再往上继续增长了。

4、忽略任务运行结果

有时候延时任务的结果我们并不想保存,但是我们配置了 result_backend 参数,这个时候我们有三种方式不保存运行结果。

1.ignore_result=True 不保存任务运行的结果

@app.task(ignore_result=True)
def add(x, y):
return x + y

2.app.conf 配置

也可以通过 app.conf 的配置来禁用结果的保存:

app.conf.update(
task_ignore_result=True
)

3.执行单个任务的时候禁用

from proj.tasks1 import add
add.apply_async((1, 2), ignore_result=True)

apply_async() 函数的作用相当于是带参数的 delay(),或者 delay() 是简化版的 apply_async(),这个我们下面会介绍。

5、task 的调用

前面简单两个简单的调用方法,一个是 apply_async(),一个是 delay()。

简单来说就是 delay() 是不带参数执行的 apply_async()。

以下用 add() 函数为例介绍一下他们的用法:

delay()

纯粹的延时任务,只能如下操作:

add.delay(1, 2)

apply_async()

带参数的用法,add() 函数的参数用 () 包起来:

add.apply_async((1, 2))

也可以带其他参数,比如上面介绍的不保存运行结果:

add.apply_async((1, 2), ignore_result=True)

这个函数还可以指定延时的时间:

countdown参数

现在开始 10s 后开始运行:

add.apply_async((1, 2), countdown=10)

eta参数

也可以用 eta 参数来指定 10s 后运行:

from datetime import datetime, timedelta

now = datetime.now()
add.apply_async((1, 2), eta=now + timedelta(seconds=10))

expires参数

这个是用来设置过期的参数:

add.apply_async((1, 2), countdown=60, expires=120)

上面的参数表示,距现在60秒后开始执行,两分钟后过期

如果想获取更多后端相关文章,可扫码关注阅读:

celery笔记三之task和task的调用的更多相关文章

  1. openwrt开发笔记三:uci移植及API调用

    1.uci编译安装.移植 安装依赖 libubox #安装cmake sudo apt-get install cmake #下载依赖库libubox git clone http://git.nbd ...

  2. Swoft2.x 小白学习笔记 (三) --- Task、协程

    介绍swoft中 1.Task 2.协程 一:Task任务: 1.配置,在 app/bean.php文件中加入 'httpServer' => [ // ... 'on' => [ Swo ...

  3. 转载 三、并行编程 - Task同步机制。TreadLocal类、Lock、Interlocked、Synchronization、ConcurrentQueue以及Barrier等

    随笔 - 353, 文章 - 1, 评论 - 5, 引用 - 0 三.并行编程 - Task同步机制.TreadLocal类.Lock.Interlocked.Synchronization.Conc ...

  4. 三、并行编程 - Task同步机制。TreadLocal类、Lock、Interlocked、Synchronization、ConcurrentQueue以及Barrier等

    在并行计算中,不可避免的会碰到多个任务共享变量,实例,集合.虽然task自带了两个方法:task.ContinueWith()和Task.Factory.ContinueWhenAll()来实现任务串 ...

  5. 《C#并发编程经典实例》学习笔记—2.8 处理 async Task 方法的异常

    异常处理一直是所有编程语言不可避免需要考虑的问题,C#的异步方法的异常处理和同步方法并无差别,同样要借助 try catch 语句捕获异常. 首先编写一个抛出异常的方法 static async Ta ...

  6. celery 笔记

    参考:https://blog.csdn.net/tichimi3375/article/details/82415412 中文翻译:https://www.celerycn.io/      htt ...

  7. Netty学习笔记(三)——netty源码剖析

    1.Netty启动源码剖析 启动类: public class NettyNioServer { public static void main(String[] args) throws Excep ...

  8. Django笔记三十三之缓存操作

    本文首发于公众号:Hunter后端 原文链接:Django笔记三十三之缓存操作 这一节介绍一下如何在 Django 中使用 redis 做缓存操作. 在 Django 中可以有很多种方式做缓存,比如数 ...

  9. Oracle学习笔记三 SQL命令

    SQL简介 SQL 支持下列类别的命令: 1.数据定义语言(DDL) 2.数据操纵语言(DML) 3.事务控制语言(TCL) 4.数据控制语言(DCL)  

  10. 《CMake实践》笔记三:构建静态库(.a) 与 动态库(.so) 及 如何使用外部共享库和头文件

    <CMake实践>笔记一:PROJECT/MESSAGE/ADD_EXECUTABLE <CMake实践>笔记二:INSTALL/CMAKE_INSTALL_PREFIX &l ...

随机推荐

  1. Windows的压缩文件夹(zip/cab)

    https://weibo.com/1114096665/DtHXgvnva #windows10# 硬要把zip.cab文件当文件夹,不爽怎么解决? 删除注册表 "HKEY_CLASSES ...

  2. 用 Go 剑指 Offer 09. 用两个栈实现队列

    用两个栈实现一个队列.队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能.(若队列中没有元素,deleteHea ...

  3. 可视化—AntV G6 紧凑树实现节点与边动态样式、超过X条展示更多等实用小功能

    通过一段时间的使用和学习,对G6有了更一步的经验,这篇博文主要从以下几个小功能着手介绍,文章最后会给出完整的demo代码. 目录 1. 树图的基本布局和使用 2. 根据返回数据的属性不同,定制不一样的 ...

  4. DataLeap 数据资产实战:如何实现存储优化?

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 背景 DataLeap 作为一站式数据中台套件,汇集了字节内部多年积累的数据集成.开发.运维.治理.资产.安全等全 ...

  5. [Maven]Maven聚合工程

    一直对此问题好奇,正好有这兴致和时间,有必要了解一下. 所谓聚合项目,实际上就是对项目分模块. 互联网项目一般来说按照业务分(订单模块.VIP模块.支付模块.CMS模块-): 传统的软件项目,大多采用 ...

  6. day110:MoFang:重新构造用户关系状态&添加好友&处理好友申请&获取申请好友历史记录&好友列表显示

    目录: 1.用户关系状态:重新构造 2.添加好友 3.处理好友申请 4.获取申请好友历史记录 5.好友列表 day109+day110所学内容流程图 1.用户关系状态:重新构造 在day109博客的前 ...

  7. Uber SRE 实践:运维大型分布式系统的一些心得

    本文是 Uber 的工程师 Gergely Orosz 的文章,原文地址在:https://blog.pragmaticengineer.com/operating-a-high-scale-dist ...

  8. Docker Compose 部署 Jenkins

    Jenkins介绍 Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具 Jenkins应用广泛,大多数互联网公司都采用Jenkins配合GitLab.Docker.K8s作为实现D ...

  9. c/c++零基础坐牢第一天

    c/c++从入门到入土(1) 开始时间2023-04-12 22:37:21 结束时间2023-04-13 00:02:26 前言:恭喜大家打开信息时代的大门,每个时代都有代表性的炫酷技能:原始时代的 ...

  10. mysql+proxysql+replication-manager的主从半同步复制+高可用+读写分离

    环境: AlmaLinux release 9.1 MySQL Community Server Ver 8.0.33 Replication Manager v2.2.40 for MariaDB ...