本页目录
消息语义
消息可能丢、可能重复——在不可靠网络上做可靠传递,三种语义是三种工程取舍。at-least-once 最容易实现但要求消费方幂等;exactly-once 需要 producer 幂等+事务+consumer 协作,本质上是在消息系统上拼出一个分布式事务。
消息系统(Kafka, RabbitMQ, NATS)中的 semantic guarantee 是分布式系统一致性的特例——同样的理论基础,不同的表现。
三种语义
At-Most-Once
Producer: 发消息 → 不等待 ACK → 不重试
Consumer: 收到消息 → process → ack (before or after processing)
消息可能丢失: producer 没收到 ack 但 broker 已接收 → producer 不重发
consumer ack before processing → crash → 消息已从队列中移除
使用:损耗容忍的场景(指标、日志、非关键通知)。
At-Least-Once
Producer: 发消息 → 等 ACK → 超时? → 重试
Consumer: process → ack → 如果 crash before ack → 消息会被 redeliver (重发给另一个 consumer)
消息可能重复: producer 重试 → broker 收到 2 份
consumer process 完成但 ack 丢失 → broker 重发 → consumer 收到 2 份
使用:大多数消息系统(Kafka, RabbitMQ confirm mode),代价是消费者必须处理重复消息。
Exactly-Once
理论上不可能在完全异步的分布式系统中保证(FLP 的推论)。实际上通过两种途径逼近:
-
Idempotent Producer (Kafka): producer 给每条消息发唯一 (producer_id, sequence_number)。broker 检查 sequence_number——重复 → 丢弃。保证:同一 producer 到同一 partition 的消息不会重复。
-
Transactional Writes: producer 的多个消息在一个 transaction 中写入 atomically(commit → all messages visible; abort → none visible)。Kafka 的
transactional.id机制实现这个。
关键是:broker 可以去重和确保原子可见,但 consumer 端的 exactly-once 语义要求 consumer 自身幂等——这意味着 consumer 不能用简单的 ORDER_STATUS = CONFIRMED 这种非幂等更新。
幂等消费者
非幂等: UPDATE inventory SET count = count + 1 WHERE product_id = 42
→ 重试 → count += 1 两次 → 错误
幂等: INSERT INTO orders (order_id, user_id, product_id, status) VALUES (123, 456, 42, 'CREATED')
→ 重试 (same order_id=123) → UNIQUE constraint → 忽略/回已存 → 正确
或者: UPDATE inventory SET count = ?, version = version + 1 WHERE product_id = 42 AND version = ?
→ optimistic lock → 第一个重试成功, 后续的 version mismatch → "Already processed"
Kafka Idempotent Producer 细节
Producer 首次向 broker 注册: 获得 producer_id (由 broker 分配, 全局递增)
每条消息: (producer_id, producer_epoch, base_sequence, [messages])
Broker:
维护每个 producer 的最后成功 sequence_number
收到新消息: if sequence > last_seen + 1 → 判定 "hole"
if sequence <= last_seen → 重复 → 丢弃 (但记录为 OK)
else → append to log
参考
- Kafka idempotent producer: kafka.apache.org/documentation/#semantics
- Kafka transactions: kafka.apache.org/documentation/#transactions
Keywords: at-most-once, at-least-once, exactly-once, idempotent producer, transactional outbox, sequence number, deduplication