本节讨论多渠道系统的工程现实:重复投递与乱序是常态。目标不是追求昂贵的“恰好一次”,而是用幂等键、提交点与专项测试把重复与重试变成无害行为,并把写入类副作用纳入可对账的状态机。文末提供三类必须做的故障注入用例,用于验证系统在重连与重试风暴中仍不会重复写入。
在多渠道、多重连、多重试的系统中,同一事件被投递多次并不罕见。导致重复的来源包括。
渠道回调重试。
网关重连重报。
消费者故障恢复后的重放。
因此,系统设计应默认至少一次,并把“重复处理”设计成可接受。
幂等键要满足三个性质。
稳定:同一业务意图在重试与重连中产生相同键。
唯一:不同业务意图不会碰撞。
有窗口:去重需要时间窗,窗口匹配业务时效与回放需求。
对于写入类工具,幂等键应成为强制入参或强制派生字段。否则任何重试都会放大副作用。
操作示例:用 Redis 做去重窗口,键名仅示意。
说明:这里使用 Redis 只是为了表达“原子写入 + TTL 去重窗口”的语义;实现可替换为任何支持等价原子语义的存储。
幂等只解决重复,不解决乱序。乱序会让旧指令覆盖新指令,尤其在同一会话的强因果链路中。
常见做法是为事件引入序列号或版本号,并在会话状态中记录 last_seq。
last_seq
如果 seq 小于等于 last_seq,视为过期事件,直接丢弃或进入缓冲。
seq
只有在跨过提交点后,才允许产生外部副作用。
提交点的存在让“提前终止”与“重试”可对账:提交点之前可安全终止,提交点之后必须走补偿或明确告知可能已产生副作用。
幂等与一致性不能只靠代码审查,必须通过故障注入验证。最小专项测试包括。
重复投递:同一事件投递两次,结果一致且无重复副作用。
乱序投递:后发先到,系统拒绝或延迟处理过期事件。
重试风暴:短时间大量失败重试,系统限速并保持可用。
这些测试应纳入回归用例库,避免配置或策略变更后悄然破坏幂等边界。
最后更新于6天前
redis-cli SET dedupe:${EVENT_KEY} 1 NX EX 600