如何在不重复的情况下重试芹菜任务 - SQS

时间:2016-05-04 23:58:08

标签: python django task celery amazon-sqs

我有一个Celery任务,它接收来自SQS队列的消息并尝试运行它。如果失败,则应该每10秒重试至少144次。我认为正在发生的是它失败并重新进入队列,同时它创建了一个新的,重复它为2.这两个再次失败并按照相同的模式创建2个新的并成为4个消息总。因此,如果我让它运行一段时间,队列就会被堵塞。

我没有得到的是在不重复的情况下重试它的正确方法。以下是重试的代码。请看看有人可以在这里指导我。

from celery import shared_task
from celery.exceptions import MaxRetriesExceededError


@shared_task
def send_br_update(bgc_id, xref_id, user_id, event):
    from myapp.models.mappings import BGC

    try:
        bgc = BGC.objects.get(pk=bgc_id)
        return bgc.send_br_update(user_id, event)

    except BGC.DoesNotExist:
        pass

    except MaxRetriesExceededError:
        pass

    except Exception as exc:
        # retry every 10 minutes for at least 24 hours
        raise send_br_update.retry(exc=exc, countdown=600, max_retries=144)

更新 对此问题的更多解释......

用户在我的数据库中创建一个对象。其他用户对该对象进行操作,当他们更改该对象的状态时,我的代码会发出信号。然后,信号处理程序启动芹菜任务,这意味着它连接到所需的SQS队列并将消息提交给队列。运行工作程序的芹菜服务器会看到新消息并尝试执行该任务。这是它失败的地方,并且重试逻辑进入。

根据celery文档retry a task,我们需要做的就是用countdown和/或max_retries引发self.retry()调用。如果芹菜任务引发异常,则认为是失败的。我不确定SQS如何处理这个问题。我所知道的是,一个任务失败,队列中有两个,两个都失败,然后队列中有4个等等......

2 个答案:

答案 0 :(得分:1)

这不是芹菜,也不是SQS问题。 真正的问题是工作流,即您向MQ服务发送消息并处理导致重复的方式。使用任何其他MQ服务您将面临同样的问题。

想象一下你的流程

  1. 脚本:读取任务消息。 MQ消息:锁定30秒
  2. 脚本:任务失败。 MQ消息:锁定超时,消息现在可以再次获取
  3. 脚本:创建另一个任务消息
  4. 脚本:重复步骤1. MQ消息:2消息具有相同的任务,因此步骤1将启动2个任务。
  5. 因此,如果任务继续失败,它将保持乘法,2,4,8,16,32 ....

    如果芹菜脚本意味着“重新创建失败的任务并发送到消息队列”,您需要确保这些消息只能读取ONCE。 **即使任务失败,您必须在已经读过1次后丢弃任务消息。 **

    至少有两种方法可以做到这一点,选择一种方法。

    1. 在重新创建任务之前删除该消息。 OR
    2. 在SQS中,您可以通过创建DeadLetter队列,配置Redrive策略,将Maximum Receives设置为1来强制执行此操作。这将确保消息 已阅读的任务永远不会回收。
    3. 您可能更喜欢方法2,因为方法1要求您将芹菜配置为“消费”(读取和删除)尽快它读取消息,这不是很实用。 (并且必须确保在为失败的任务创建新消息之前将其删除) 这个死信队列是一种让你检查芹菜CRASH,即已被读过但未被消费(删除)的消息的方法,意味着程序在某处停止。

答案 1 :(得分:0)

这可能有点晚了,我为Celery + SQS编写了一个补丁修补程序。

您可以看到它如何在此存储库中实现

https://github.com/galCohen88/celery_sqs_retry_policy/blob/master/svc/celery.py