消息投递语义
对比 at-most-once、at-least-once 与 exactly-once 语义,解释消息确认、重试和故障恢复为什么会把“是否丢消息”与“是否重复处理”变成工程取舍。
#synthesis
#growing
#backend
#architecture
[!info] related notes
- 所属 MOC:Messaging & Queues MOC
- 消费端:幂等消费者
- 具体实现:Apache Kafka、RabbitMQ、Redis Stream
- 跨系统:分布式事务
消息投递语义
[!abstract] 核心问题 投递语义描述在发送、处理、确认和故障恢复之间,消息可能被处理零次、一次或多次。它不是 Broker 的单个开关;最终业务效果还取决于 Consumer、数据库和外部 API 的事务与幂等边界。
At-most-once
ACK / advance progress first
→ process later
失败时消息可能丢,但不会因为 Broker 重投而重复处理。
适合:
- 可丢 Telemetry;
- 高频状态采样;
- 重复比丢失代价更高、且可以接受缺口的场景。
At-least-once
process first
→ ACK / commit progress after success
如果业务处理成功后、ACK 或 Offset Commit 前崩溃,消息会再次交付。
优点:尽量不丢业务处理;代价:Consumer 必须接受重复。
这是生产消息系统中非常常见的组合:
At-least-once Delivery
+ Idempotent Consumer
Exactly-once 为什么容易被误解
必须先定义边界:
Producer → Broker?
Broker internal processing?
Consumer Offset + Kafka output?
Consumer → PostgreSQL?
Consumer → payment API?
一个系统可以保证 Broker 内部事务性,却无法自动让外部数据库和第三方 API 与它原子提交。
更实用的目标通常是:
消息可能重复交付,但业务副作用通过唯一约束、幂等键、状态机或事务绑定,只产生一次有效结果。
ACK / Offset 的两个故障窗口
先确认,再处理
ACK / commit
→ crash before business effect
→ message not redelivered
风险:业务效果丢失。
先处理,再确认
business effect succeeds
→ crash before ACK / commit
→ message redelivered
风险:重复执行。
网络不可靠时无法同时凭空消除两个窗口,必须通过事务、幂等或 Reconciliation 缩小影响。
Producer 也有未知结果
Producer sends
→ Broker persists
→ response lost
→ Producer cannot know whether publish succeeded
安全重试需要稳定 Message ID、Idempotent Producer 或 Broker/业务层去重;否则可能产生重复消息。
顺序与投递语义是不同维度
即使 At-least-once 工作正常,也可能出现:
- Retry 导致旧消息晚到;
- 多 Consumer 并发完成顺序不同;
- 跨 Partition 没有全局顺序;
- DLQ Replay 把历史事件重新带回。
需要 Entity Version、Sequence、State Machine 或 Conflict Policy 处理顺序,而不是只做去重。
Broker 与业务数据库的边界
consume record
→ write business DB
→ commit progress
想减少“双写”窗口,可采用:
- 同一数据库中的 Processed Message + Business Update 事务;
- Kafka 内部 Transaction(只覆盖其支持边界);
- Transactional Outbox / CDC;
- Idempotency Key + Reconciliation;
- Saga / Compensation。
设计检查表
- 明确允许丢失、重复和乱序中的哪些风险。
- Producer、Broker、Consumer、DB 的确认点分别在哪里。
- Message ID 与业务 Idempotency Key 稳定。
- Consumer 成功的定义是业务提交,而不是函数未抛错。
- Retry / DLQ 不破坏顺序和版本规则。
- 指标能观察 Redelivery、Duplicate、Lag、DLQ 和最终业务结果。
- 文档中的 “exactly-once” 明确写出系统边界。