Celery 死信队列 Dead‑Letter Queue(DLQ)

Celery 死信队列 Dead‑Letter Queue(DLQ)

通俗理解:正常队列放不下、处理不了的消息,丢到死信队列。存放那些确定处理失败、不能再重试的任务消息。

前提:Celery本身没有开箱即用的死信队列,需要借助broker的能力(Redis / RabbitMQ)手动配置。

什么消息会进死信

  1. 任务多次重试,重试次数耗尽,还是失败,不再继续重试。
  2. 消息过期(超过TTL)。
  3. 消息被拒绝,不允许重新入队。
  4. 队列满了,消息无法投递。

正常流程:任务 → 业务队列(work_queue) → worker取出来执行。
失败重试:抛出异常 → 重新放回原队列,反复重试。
重试耗尽:不再放回原业务队列,路由转发到死信队列 dlq

死信队列里面的消息,不会被普通worker消费
需要人工观察、排查问题:参数错误、业务bug、第三方接口挂掉;修复完之后,可以把死信消息重新放回业务队列重新执行。

对比普通失败 vs 死信

  • 普通失败+重试:还抱有希望,放回原队列,等下一次worker拿出来再跑。
  • 死信:已经放弃自动重试,交给人工处理

不要让消息无限重试!无限重试会疯狂打数据库、打第三方接口,雪崩。所以设置最大重试次数,耗尽就丢死信。

RabbitMQ 和 Redis 的区别

  1. RabbitMQ:原生完整支持死信交换机 DLX,配置简单。
    消息满足条件,通过死信交换机路由到死信队列。

  2. Redis broker(celery默认常用)
    Redis本身没有原生死信队列。
    Celery‑Redis 实现死信,一般两种方案:

  • 方案A:任务重试耗尽之后,手动在 on_failure钩子里面,把任务手动push到另一个独立redis list,作为死信队列
  • 方案B:使用单独的死信worker,不推荐。

很多人踩坑:用Redis做broker,不配置死信;重试耗尽之后任务直接丢掉,消息直接消失,找不到。

简单代码模拟(Redis场景,手动实现死信)

from celery import Task
import redis

r = redis.Redis(decode_responses=True)
DEAD_LETTER_QUEUE = "dead_letter_queue"

class BaseTaskWithDLQ(Task):
    autoretry_for = (Exception,)
    retry_backoff = 1
    retry_max = 3  # 最多重试3次,耗尽触发on_failure

    def on_failure(self, exc, task_id, args, kwargs, einfo):
        # 重试耗尽,进入on_failure钩子
        print(f"任务{task_id}彻底失败,送入死信队列,参数 {args}")
        # 手动把任务信息存入死信队列redis list
        r.lpush(DEAD_LETTER_QUEUE, str({
            "task_id": task_id,
            "args": args,
            "kwargs": kwargs,
            "error": str(exc)
        }))

@shared_task(base=BaseTaskWithDLQ)
def test_task(x):
    1 / x

逻辑:

  1. 抛出异常,自动重试,最多3次。
  2. 3次全部失败,进入on_failure钩子。
  3. 在钩子里面把任务信息推到独立redis list,充当死信队列。
  4. 普通worker不会消费这个dead_letter_queue

死信队列配套运维操作

  1. 定时告警:监控死信队列长度,长度>0就告警,通知开发。
  2. 排查:查看死信里面的任务id、参数、报错。
  3. 修复bug之后,把死信消息重新放回业务队列重新消费
  4. 清理旧的死信消息。

重要误区

  1. ❌死信队列不会自动重新执行任务。死信是保存失败消息,等待人工介入,不会自己恢复。
  2. on_failure钩子本身不等于死信队列。钩子只是通知;需要你自己把消息存储下来,才形成死信。
  3. ❌Redis broker不会自动把消息丢死信,必须自己实现;RabbitMQ可以通过DLX自动路由。
  4. 如果任务内部捕获异常吞掉异常,不抛出去:不会重试,也不会进死信。

和之前知识点串联

  1. on_failure 钩子:事件回调,只是收到任务彻底失败的通知,没有保存消息的能力;我们利用这个钩子把消息存入死信存储。
  2. 钩子是通知;死信队列是存储失败消息的容器
  3. 中间件可以在任务执行前拦截;死信是任务已经彻底失败之后的归宿。

通俗比喻

快递派送:
正常任务 = 快递派送。
重试 = 派送失败,第二天再派送一次。
重试多次全部失败 → 快递无法送达,放到无人认领包裹仓库(死信队列)
不会反复派送;需要人去仓库查看包裹,确认地址修好之后,再重新派送。

什么时候需要死信队列?

业务场景:任务不能丢,失败之后希望保留现场,不能直接消失。
比如订单回调、数据同步任务;如果不做死信,重试耗尽任务直接丢失,业务数据就缺了。

简单总结:

死信队列就是专门存放重试已经全部失败,不再自动处理的消息的队列;目的是保存现场,告警,留给人工排查和恢复。RabbitMQ原生支持;Redis做broker需要借助on_failure钩子手动实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

草莓仙生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值