本页目录

消息语义

消息可能丢、可能重复——在不可靠网络上做可靠传递,三种语义是三种工程取舍。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 的推论)。实际上通过两种途径逼近:

  1. Idempotent Producer (Kafka): producer 给每条消息发唯一 (producer_id, sequence_number)。broker 检查 sequence_number——重复 → 丢弃。保证:⁠同一 producer 到同一 partition 的消息不会重复⁠。

  2. 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