← 中文资源

简体中文版本

幂等消费者

至少一次投递意味着重复是常态;幂等消费者用稳定消息键和事务化去重表,把重复投递折叠为一次有效状态变更。

阅读英文原文

重复是常态

至少一次投递保证消息会送达,但不保证只送一次。消费者可能处理完却在确认前崩溃,于是 broker 重新投递;超时后的重试也可能发生在第一次已经成功之后。幂等消费者把这些重复转成一次有效状态变更。

CREATE TABLE processed_messages (
  consumer   text NOT NULL,
  message_id text NOT NULL,
  processed_at timestamptz NOT NULL DEFAULT now(),
  PRIMARY KEY (consumer, message_id)
);

BEGIN;
INSERT INTO processed_messages (consumer, message_id)
VALUES ('order-projector', $1);
UPDATE order_totals SET total = total + $2 WHERE order_id = $3;
COMMIT;

去重插入与业务更新在同一事务中。如果主键冲突,说明消息已处理过,直接确认而不执行更新;如果事务失败,两者一起回滚,broker 可以重新投递。

选择稳定的键

使用 broker 的消息 id 或生产者的领域事件 id,不要使用负载哈希。负载哈希会因新增字段或格式变化而改变,也无法识别语义相同的重试。键必须在重复投递间保持稳定,并对同一逻辑事件唯一。

保留期很重要。高流量系统不能永久保存已处理 id。保留时间应长于 broker 的最大重投递窗口,按时间分区表,并归档或删除旧行。如果消息可能在保留期后重投,去重就无能为力;必须由 broker 配置限制重投递。

让效果本身也幂等

去重防的是同一个消息 id,但消费者可能处理两条代表同一业务动作的不同消息。尽量使用幂等操作:设置值而不是盲目自增,或在领域模型中带上版本或幂等键。对于计数器,保存贡献事件 id 或按聚合维护版本,避免重复自增。

何时使用

任何可能重投递且效果并非天然幂等的队列或 webhook,都应使用幂等消费者。优点是安全重试和更简单的故障恢复;代价是去重存储、事务耦合和保留策略。与依赖 exactly-once 基础设施相比,应用层去重可跨 broker 移植,并让保证显式化。