Cloudflare Queues 消息处理失败后会怎样?
我在本地把 max_retries 设成 2、retry_delay 设成 1 秒,让一条消息每次都调用 retry()。它一共被投递了 3 次,attempts 依次是 1、2、3,间隔约 2 秒,然后进了死信队列。
所以答案是:消息会按 max_retries 重试,重试用完后进入死信队列;没配死信队列,就会被永久删除。 注意 max_retries: 2 在我本地对应的是总共 3 次投递。
重试、死信各有几个细节容易踩,官方文档在其中一个地方措辞还不一致。下面按处理失败后发生的顺序讲。
重试是怎么配置的?
配置里 max_batch_size、max_batch_timeout、max_retries、retry_delay、dead_letter_queue 都是消费者的字段:
{
"name": "jobs-worker",
"main": "src/index.ts",
"compatibility_date": "2026-10-01",
"queues": {
"producers": [{ "queue": "jobs", "binding": "JOBS" }],
"consumers": [
{
"queue": "jobs",
"max_batch_size": 10,
"max_batch_timeout": 5,
"max_retries": 3,
"retry_delay": 30,
"dead_letter_queue": "jobs-dlq",
"max_concurrency": 5
}
]
}
}官方文档给的默认值和范围是:max_batch_size 默认 10,上限 100;max_batch_timeout 默认 5 秒,上限 60;max_retries 默认 3,上限 100。批量大小和超时先到先触发。
max_retries 到底对应几次投递?
我本地的结果是:max_retries: 2 对应总共 3 次投递,也就是总尝试次数是它加一。
官方文档在这一点上措辞并不一致:配置页写的是重试三次后标记为失败,而 pull 消费者一节写的是 attempts 达到 max_retries 时消息被删除。两种读法差一次。我没法替官方裁决,线上以官方为准。写业务逻辑时不要依赖精确的总次数,只依赖“最终会进入死信队列或被删除”。
想要退避重试,怎么写?
用 msg.attempts 算延迟,再传给 retry()。attempts 在我本地从 1 开始。
export default {
async queue(batch: MessageBatch<{ orderId: string }>, env: Env) {
for (const msg of batch.messages) {
try {
await handle(msg.body, msg.id, env);
msg.ack();
} catch (e) {
// delaySeconds 官方最大 24 小时,这里自己封顶 1 小时
msg.retry({ delaySeconds: Math.min(2 ** msg.attempts * 10, 3600) });
}
}
},
} satisfies ExportedHandler<Env>;逐条 ack() 和 retry(),让处理器正常返回。如果靠抛异常表达失败,一批里的好消息会被连带重放,原因见 Cloudflare Queues 会重复投递消息吗?。
重试用完后,消息去哪了?
默认是被永久删除。配置了 dead_letter_queue 才会转入该队列。死信队列本身就是一个普通队列,可以单独消费。
官方文档里几个细节:
- 死信队列必须与源队列在同一个 jurisdiction。
- 名字不存在时会自动创建。
- 没有消费者的死信队列里,消息保留四天后删除。
- 每次重试按一次读操作计费,进入死信队列算一次写入。
我在本地看到,死信队列的消费者收到这条消息时,attempts 重新从 1 开始。
怎么让死信队列真正有用?
死信队列不是垃圾桶,是告警入口。给它配一个消费者,至少记录并报警。没有消费者的话,消息四天后会无声消失,你连它存在过都不知道。
另外,官方文档说的重试间隔和调度细节,本地 wrangler dev 里没法复现,上线前别拿本地的耗时当依据。
本地部分是我在 wrangler dev 里跑的:max_retries: 2总共投递 3 次、attempts为 1、2、3、间隔约 2 秒、死信队列里attempts重新从 1 开始。默认值与范围、死信队列的 jurisdiction、自动创建、保留四天和计费,都是官方文档的说法,我没有在线上验证;线上真实的重试间隔与调度,本地也无法复现。官方文档在重试次数上的措辞不一致,以官方为准。限额与默认值会变化,以官方文档为准。