之前讲到利用celery异步处理一些耗时或者耗资源的任务,但是近来分析数据的时候发现一个奇怪的现象,即是某些数据重复了,自然想到是异步任务重复执行了。

查阅之后发现,到如果一个任务太耗时,任务完成时间超过了broker的时间(Redis默认为一小时)则任务会被再次分配到worker.

Visibility Timeout

The visibility timeout defines the number of seconds to wait for the worker to acknowledge the task before the message is redelivered to another worker. Be sure to see Caveats below.

This option is set via the broker_transport_options setting:

app.conf.broker_transport_options = {'visibility_timeout': 3600}  # 1 hour.

The default visibility timeout for Redis is 1 hour.

问题在于我的应用中的异步任务耗时绝不可能超过Redis默认的一小时,那么问题出在这个“Acknowledge”了,一开始我的理解是这个acknowledge是worker收到了broker发送的任务。但是通过查看workererr.log 发现:

[2019-03-01 14:20:30,695: INFO/MainProcess] Received task: task_async[4e0378e2-ff5d-4394-a842-ece2d1c8118a] ETA:[2019-03-01 15:44:03.692831+08:00]

[2019-03-01 15:23:58,477: INFO/MainProcess] Received task: task_async[4e0378e2-ff5d-4394-a842-ece2d1c8118a] ETA:[2019-03-01 15:44:03.692831+08:00]

[2019-03-01 15:44:04,620: INFO/ForkPoolWorker-2] Task task_async[4e0378e2-ff5d-4394-a842-ece2d1c8118a] succeeded in 0.003580662072636187s: None

[2019-03-01 15:44:04,621: INFO/ForkPoolWorker-1] Task task_async[4e0378e2-ff5d-4394-a842-ece2d1c8118a] succeeded in 0.004984764964319766s: None

1. 重复执行的任务被发送了多次 (时间间隔为1小时)

2. worker多次接收到同样的任务(同ID),并且几乎一样的ETA(预计执行时间)

3. 在ETA到达之后,这个任务会被多个子线程认领并执行,每次执行时间并不长

所以为什么14:20:30任务接收到之后15:23:58任务再次发送呢,问题在约“Acknowledge”(认领)并不是以“Received”为结束标志的,看celery对于acknowledge的解释:

acknowledged

Workers acknowledge messages to signify that a message has been handled. Failing to acknowledge a message will cause the message to be redelivered. Exactly when a transaction is considered a failure varies by transport. In AMQP the transaction fails when the connection/channel is closed (or lost), but in Redis/SQS the transaction times out after a configurable amount of time (the visibility_timeout).

所以说是以“Handled”来进行判定而非任务已被接收,所以会出现当我的定时任务在一小时后才执行的情况下,第一次发送的任务虽然接受了但是并未执行(Acknowledge),所以一小时后任务再次被发送。

解决这个问题的时候回看celery开篇教程中的一段:

Ideally task functions should be idempotent: meaning the function won’t cause unintended effects even if called multiple times with the same arguments. Since the worker cannot detect if your tasks are idempotent, the default behavior is to acknowledge the message in advance, just before it’s executed, so that a task invocation that already started is never executed again.

最佳实践中的任务应该是幂等的!

总结起来:

1. Task received的时候并不是acknowledge的时候,而task执行才是acknowledge (任务才会从broker队列中移除).

2. 我的任务都是定时任务(超过一小时),所以我设置visibility_time 超出我的定时,则重复执行不会再发生.

3. 如果任务很长或者跨度很长,如果对于只执行一次有严格要求,可以参考celery_once.

4. 还是要仔细阅读官方文档!!

Ref:

Scheduled tasks are being duplicated

https://github.com/cameronmaske/celery-once

http://docs.celeryproject.org/en/latest/getting-started/brokers/redis.html#visibility-timeout

https://github.com/celery/django-celery/issues/176

Celery异步任务重复执行(Redis as broker)的更多相关文章

  1. Celery异步处理

    1.Celery概述 1.1问题抛出 我们在做网站后端程序开发时,会碰到这样的需求:用户需要在我们的网站填写注册信息,我们发给用户一封注册激活邮件到用户邮箱,如果由于各种原因,这封邮件发送所需时间较长 ...

  2. celery异步任务框架

    目录 Celery 一.官方 二.Celery异步任务框架 Celery架构图 消息中间件 任务执行单元 任务结果存储 三.使用场景 四.Celery的安装配置 五.两种celery任务结构:提倡用包 ...

  3. django、celery异步发邮件

    django.celery异步发邮件 django自带的send_mail发邮件功能执行发邮件功能会因为网络的原因造成花费的时间过长,为了解决这个问题,可以用celery + redis代替 安装包: ...

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

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

  5. celery异步消息处理框架

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

  6. Celery 异步任务

    Celery https://www.cnblogs.com/DragonFire/p/10356615.html 介绍: Celery 是芹菜 Celery 是基于Python实现的模块, 用于执行 ...

  7. Celery 异步任务 , 定时任务 , 周期任务 的芹菜

    1.什么是Celery?Celery 是芹菜Celery 是基于Python实现的模块, 用于执行异步定时周期任务的其结构的组成是由    1.用户任务 app    2.管道 broker 用于存储 ...

  8. celery异步认知

    celery是异步任务的框架 是由python实现的异步框架. 在使用celery我们经常分为三个部分, 第一部分就是我们所说的客户端, 就是发起异步任务的一方, 第二部分 任务队列 broker 第 ...

  9. Django商城项目笔记No.6用户部分-注册接口-短信验证码实现celery异步

    Django商城项目笔记No.4用户部分-注册接口-短信验证码实现celery异步 接上一篇,如何解决前后端请求跨域问题? 首先想一下,为什么图片验证码请求的也是后端的api.meiduo.site: ...

随机推荐

  1. YTST_CX_0001(ALV栏位汇总)

    *********************************************************************** * Title           : X        ...

  2. Java for LeetCode 106 Construct Binary Tree from Inorder and Postorder Traversal

    Construct Binary Tree from Inorder and Postorder Traversal Total Accepted: 31041 Total Submissions: ...

  3. 编写你的第一个web应用程序1

    在shell中运行以下命令来检查django是否已安装及其版本 python -m django --version 如果django已经安装,你应该看到安装的版本号,如果还没有安装,你会看到一个‘n ...

  4. ubuntu删除文件夹

    -r表示强制删除,-f表示不提示 强制删除文件夹并提示 sudo rm -r 文件夹名 强制删除文件夹不提示 (最暴力) sudo rm -rf 文件夹名

  5. SQL语法之初级增删改查

    SQL语法之初级增删改查 1.增 1.1插入单行 INSERT INTO [表名](列名) VALUES(列值) 语法如下: INSERT INTO bsp_Nproductclass(guid,pi ...

  6. 20145239杜文超 《Java程序设计》第10周学习总结

    20145239 <Java程序设计>第10周学习总结 教材学习内容总结 Java的网络编程 网络编程 网络编程就是在两个或两个以上的设备(例如计算机)之间传输数据. 网络概述 1.计算机 ...

  7. 简单的C++程序题总结

    1.求一个数的二进制中1的个数. 思想的关键在于x=x&(x-1)这里,例如二进制为0x0729,即x=0000 0111 0010 1001,那么x-1=0000 0111 0010 100 ...

  8. python的小知识点

    python中的变量的名字必须由字母.数字.下划线组成,并且不可以以数字开头. 字典的内容是键-值对,键必须是不可变的,比如字符,整数,浮点数,元组,列表不可以,因为列表可变.集合的元素不重复.字典和 ...

  9. CI公用模型

    <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); /** * SEO管理系统 -- 公用模型 ...

  10. 存储过程IF --ELSE IF -- END IF 使用

    CREATE OR REPLACE PROCEDURE BJPJYXK_HF_SD( sqid_p IN VARCHAR2,--申请单ID xkbh_p IN VARCHAR2,--新生成的许可证编号 ...