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场景,手动实现死信)

python 复制代码
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钩子手动实现。

相关推荐
Python私教1 小时前
四个入口,一条流水线:如意智影的多入口架构取舍
人工智能·python·架构
名字还没想好☜1 小时前
用 Python struct 解析二进制协议:pack/unpack、字节序与对齐踩坑
开发语言·python·二进制·struct
青 春 记 忆2 小时前
LeetCode 155. 最小栈|Python 解法详解
开发语言·python·leetcode
程序员三藏11 小时前
Python+requests实现接口自动化测试
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·接口测试
一直走下去-明13 小时前
简单的http抓包解包完整代码
开发语言·python
雷帝木木13 小时前
数据湖与数据仓库:从理论到实践
人工智能·python·深度学习·机器学习
数字化转型分享点滴13 小时前
机加工车间上线 SH‑AIOT 物联网会遇到哪些常见实施难点
python·物联网
05664614 小时前
agent学习——流式响应与文本切分
网络·python·学习
OPEN-F14 小时前
Python进阶教程:正则表达式进阶与文本处理
开发语言·python·正则表达式