第 10 / 19 篇

第 9 章 可靠性:一条消息究竟算不算送达

先给成功一个边界

生产者返回成功、Broker 保存记录、所有副本完成复制、消费者读到、业务数据库更新成功,是不同阶段。讨论“不丢”或“不重复”时,必须说明在哪个阶段、容忍哪些故障。

生产者发出请求但没有收到回应,也不等于 Broker 没收到:请求可能成功了,只是响应在网络里丢了。这也是重试可能制造重复的原因。

acks、副本和 ISR

acks=0 不等待服务端确认,吞吐需求不能让它自动变可靠。acks=1 等待 Leader 确认,但此时尚未复制的数据可能在 Leader 故障时丢失。acks=all 等待当前同步副本集合 ISR 的确认。

ISR 是与 Leader 保持足够同步的副本集合。min.insync.replicas 决定允许接受这类可靠写入的最低 ISR 数量。常见设计起点是副本数 3、min.insync.replicas=2、生产者 acks=all:当 ISR 小于 2 时拒绝写入,以牺牲部分可用性保护可靠性。这仍要求正确的故障域、存储、选主等条件。

本书单节点只有一个副本,不能靠把 acks 调成 all 得到三台机器的保护。也不能在只有一个副本的主题上设 min.insync.replicas=2 后期待照常写入。

生产者幂等

Kafka 4.1 Java 生产者在没有冲突配置时默认启用幂等。显式 enable.idempotence=true 时,相关条件包括 acks=all、retries>0、max.in.flight.requests.per.connection<=5。不要照搬“必须把 in-flight 设成 1”的旧教程。

生产者幂等主要避免协议重试造成的日志重复。它不识别业务上两次分别调用 send 的同一订单事件,也不自动防止消费者重复发短信。

建议应用显式检查发送完成结果,对异步发送处理回调或 Future;只把记录放进客户端缓冲区就立刻退出程序,不能证明记录已成功送达。

三种处理语义

语义 典型失败结果 适用思考
至多一次 可能漏处理,通常不重试 能接受丢失的场景才考虑
至少一次 尽力避免漏处理,但可能重复 配合业务幂等很常见
恰好一次 在明确事务边界内只产生一次有效结果 必须讲清系统和故障假设

“恰好一次”不是万能开关。Kafka 读入、处理、写回 Kafka 的链路可以把输出与消费进度放进同一个 Kafka 事务;下游需要适当使用 read_committed。Kafka Streams 也提供相应的处理保证。普通数据库更新或外部支付接口不自动包含在这个事务里。

本章自测:设置生产者幂等后,短信还能重复吗?能。短信副作用与消费进度之间仍需业务设计。