可靠性#
一条消息投出去之后可能执行失败或超时。这一页说明两类情形的处置: 失败重试,以及尚未落地的死信队列(DLQ)。它们在链路中的位置见 架构。
尝试次数#
Message 带一个 attempt 字段(默认 1),它随 payload 一起序列化,因此跨进程、
跨重投都跟着消息走。每投递一次算一次尝试:
客户端首次投递:
attempt = 1;Worker 执行失败或超时:
attempt += 1,未超出上限则退避后重投;reclaim把滞留消息重投给新消费者时同样attempt += 1。
失败重试#
任务抛异常、或执行时间超出 task_timeout 时,Worker 按以下顺序处理:
记一条失败日志;
attempt += 1;若
attempt <= max_attempts(默认3),按退避延时把消息重新投给队列 —— 这次尝试在队列里是一条新 entry;若超出上限,记
max attempts reached并丢弃该消息(当前止于日志,见下文 DLQ)。
消息在队列里滞留超过 task_timeout(即 enqueued_at 太旧)时走同一条重投路径:它不会
被执行,而是带着新的时间戳重新入队。
from binomic.config import BinomicConfig
# 单条消息最多尝试 3 次(首次 + 2 次重投)
config = BinomicConfig(queues=["default"], max_attempts=3)
备注
重投不是立即发生的:Worker 按退避延时投递,首跳 1.5 秒,此后逐次翻倍 (3 / 6 / 12 / 24 秒),封顶 30 秒。退避由 Worker 自己持有的调度器承担 —— 它把 「重新入队」排成一个延时作业,而原件在排程之后就已经 ack。由此有两个后果:
退避窗口内 Worker 进程若退出,该次重试不会发生 —— 换来的代价是绝不重复投递;
该延时作业被排在「迟到也执行」的位置上,不会因为事件循环繁忙而被静默跳过。
退避间隔目前是固定值,不可配置;需要别的节奏时,请在任务函数内部自行等待。
有两类失败不重试,只记日志并 ack(消息被丢弃)—— 重投同样的 payload 不会改变结果:
消息无法反序列化(payload 损坏);
任务名未注册(
TaskNotFoundError)。
取消不 ack:Worker 关闭或任务被取消时,消息留在 PEL 里而不是被 ack,交给下一次
reclaim 重投给其它消费者。
死信队列(尚未实现)#
max_attempts 耗尽,以及上面两类不重试的失败,目前都只记日志然后丢弃;代码中留有
TODO: send to DLQ 标记。在 DLQ 落地之前,这些消息不可恢复 —— 需要保留时,请在任务
函数内部自行捕获异常并落盘或另投队列。