本页译自英文,由模型翻译。若与英文原文存在差异,以英文原文为准。
结算
ack、retry、retry_after 和 drop 在 SQS 上变成什么:每种结果都是针对队列的一次调用,重试在队列本身中等待。
以文字阅读本图
- 结果: ack; retry(); retry_after(d); drop; 未结算 就被丢弃
- SQS 调用: DeleteMessage; ChangeMessage Visibility(0); ChangeMessage Visibility(d); DeleteMessage; 达到上限时: Visibility(0); 不发调用, 延长停止
- 效果: 消失; 立即可见; d 之后回来, 不晚于接收后 12 小时; 消失, 或移入 死信队列; 超时到期 后回来
- ack → DeleteMessage
- DeleteMessage → 消失
- retry() → ChangeMessage Visibility(0)
- ChangeMessage Visibility(0) → 立即可见
- retry_after(d) → ChangeMessage Visibility(d)
- ChangeMessage Visibility(d) → d 之后回来, 不晚于接收后 12 小时
- drop → DeleteMessage; 达到上限时: Visibility(0)
- DeleteMessage; 达到上限时: Visibility(0) → 消失, 或移入 死信队列
- 未结算 就被丢弃 → 不发调用, 延长停止
- 不发调用, 延长停止 → 超时到期 后回来
retry_after(d) 以整秒计算延迟并向上取整,因此消息绝不会早于 d 回来。超出接收后 12 小时的延迟会缩短到这 12 小时结束为止,本 crate 同时记录一条注明队列名称的警告日志。未结算就被丢弃的投递不发出任何调用:它的延长停止,消息在当前超时到期时回来。
SQS 没有只拒绝而不删除消息的操作,所以 drop 会删除消息。注册声明了重新驱动策略的队列是例外。在这种队列上,已被接收 maxReceiveCount 次的投递会被交还而不是删除,因为正是下一次接收把它移入死信队列,删除则会让它丢失。
投递的 redelivery_count 就是队列的 ApproximateReceiveCount,第一次投递算作一次。它统计每一次接收,包括超时到期后的接收,而不仅是服务请求的重试。
重试
以文字阅读本图
- 一次投递 接收计数表明这是第几次尝试 → 处理器返回 retry_after(d) 或 retry(): 已处理
- 处理器返回 retry_after(d) 或 retry() → ChangeMessageVisibility(d) 消息在队列中等待: 一次重试
- ChangeMessageVisibility(d) 消息在队列中等待 → 下一次接收:这条消息是否已被 接收 maxReceiveCount 次?: d 之后
- 下一次接收:这条消息是否已被 接收 maxReceiveCount 次? → 还没有: 再次投递, 仍是同一条消息: 还没有
- 下一次接收:这条消息是否已被 接收 maxReceiveCount 次? → 是:SQS 把它 移入死信队列: 是
- 在最后一次允许的接收上 drop,会以可见性 0 交还消息, 于是 SQS 移走它,而不是丢失它
- 队列保存着等待中的消息: 服务崩溃不会丢失任何重试
重试从不离开队列。retry_after(d) 让消息在 d 内不可见,然后它作为同一条消息回来,id 不变,接收计数加一。没有任何东西被重新发布,因此运行时的重新发布回退在这里从不运行,.out_retry(..) 在 SqsQueue 上无法编译。可见性的结束时间晚于接收后 12 小时时,SQS 会拒绝它。
max_attempts(n) 与 dead_letter(q) 合起来就是队列的重新驱动策略:maxReceiveCount 是 n,死信队列是 q。订阅打开时把这个策略写到队列上,由 SQS 统计接收次数,并自行移走用尽次数的消息。只设其中一项,启动时就会得到 SqsError::IncompleteRedrive。
队列保存着等待中的消息,所以服务崩溃不会丢失任何重试:消息在可见性结束时回来,交给下一个轮询的消费者。