《Java 消息中间件·可靠消息与消费治理》目录与学习路径

卷二全 50 篇的完整目录:五部分、15 章、45 节,加序、前言与两个附录。每章标注它回答的那一个问题,并给出三条阅读路径——赶时间只看判据的、要落地一条链路的、准备做演练与验收的,另外标明哪些实验可以跳过、哪些必须亲手做,以及每节末尾那条判据该怎么用。

这本书的骨架

卷二回答一个问题:一条消息从产生到落进下游,中间哪几段会出事,每一段怎么保证、怎么验证、要付多少钱。

这个问题的答案不能堆成一张清单,得按链路顺序展开。所以全书按「语义 → 治理 → 数据链路 → 性能 → 演练」分五部分,前三部分是链路的纵深,后两部分是运营的横切。

部分章节回答的问题
第一部分第 1–3 章每段各自能保证什么?重复从哪来?事务的边界在哪?
第二部分第 4–6 章消费组怎么组织?积压了怎么办?失败的消息去哪?
第三部分第 7–9 章业务库与消息怎么对齐?事件契约怎么演进?跨服务怎么一致?
第四部分第 10–12 章顺序能不能保?消息多大算大?消费端怎么调?
第五部分第 13–15 章多租户怎么隔离?出事怎么演练?怎么验收?

前三部分是「设计」,后两部分是「运营」。只读前三部分能写出正确的链路,只读后两部分能在出事时救火,但两者都读才谈得上「可治理」。

每一章回答的那个问题

十五个章节,每一章只回答一个问题。这句话不是修辞,而是写作时的取舍标准——凡是不能回答这个问题的内容,都不写进这一章。

章问题
1端到端的「不丢」,到底该由谁保证?
2重复是必然的,那怎么让它无害?
3Kafka 事务能保证到哪一步,剩下的谁管?
4消费者该怎么分组、怎么扩缩、怎么停?
5积压到多少该扩容,到多少该跳过?
6处理失败的消息,怎么才能不阻塞整条链路?
7业务库写成功、消息发失败,怎么补救?
8事件的字段要改,老消费者怎么办?
9跨服务的一致性,怎么度量而不是怎么声称?
10顺序要不要保,保到什么粒度,代价是多少?
11一条消息多大算大,大了怎么办?
12消费端慢,是参数问题还是模型问题?
13多个业务共用一个集群,怎么不互相拖垮?
14故障演练该演练什么,怎么记录?
15一条链路能不能上线,用什么判据?

前置:三篇必须先读的

篇标题作用
序可靠性是设计出来的,不是配置出来的三个「只对了一半」的误判;四段责任模型;本卷的验证规矩
前言实验环境与故障注入口径单节点与三节点怎么起;stop/kill/pause/改配置四种手段各模拟什么
本节目录与学习路径全卷地图与三条读法

前两篇加起来不到六百行,但它们决定了后面四十五章的可信度。序告诉你「为什么配置不够」,前言告诉你「怎么把东西弄坏才能观察」。跳过它们的读者,往往会在第一次实验对不上时怀疑书。

第一部分:可靠性语义(第 1–3 章)

这一部分建立词汇和责任划分。读完它,你应该能在设计评审上准确说出「这一段由谁保证、破了会怎样」。

第 1 章 责任边界

  • 1.1 端到端可靠性的责任边界 —— 生产者、broker、消费者、下游存储四段,每段各能保证什么,「消息不丢」到底指哪一段。
  • 1.2 生产端:acks、重试与幂等 —— acks=all 的三个前提;retries 与 delivery.timeout.ms 的相互作用;幂等生产者覆盖的是「重试重复」而不是「业务重复」。
  • 1.3 消费端:手动提交与位移管理 —— 先处理后提交、先提交后处理各自的破绽;自动提交会丢什么;提交失败怎么重试。

这一章的核心实验是「杀 leader 下 acks=1 与 acks=all 的丢失范围对照」,以及「自动提交在崩溃后丢了哪些」。两个实验都用 docker kill,因为只有断电才能暴露确认窗口。

第 2 章 重复与幂等

  • 2.1 重复的四种来源 —— 生产重试、消费者再平衡、位移回退、下游重放;每种在日志里长什么样。
  • 2.2 幂等消费:唯一键、状态机与去重表 —— 三种实现各自的成本与边界;去重表的 TTL 与容量;状态机的幂等迁移。
  • 2.3 恰好一次的两种实现与成本 —— Kafka 事务 vs 业务幂等;各自成立的前提;为什么「事务也不等于业务恰好一次」。

这一章的立场很明确:重复不可避免,只能让它无害。所以 2.2 会给出一份完整的 IdempotentConsumer,2.3 会把两种方案的边界画成一张表。

第 3 章 事务

  • 3.1 事务生产者与 read_committed —— transactional.id、initTransactions、begin/commit/abort 的完整流程;两种隔离级别看到什么。
  • 3.2 事务超时、fencing 与 abort —— transaction.timeout.ms;fencing 怎么防僵尸写入;abort 之后的数据形态。
  • 3.3 事务的性能与运维代价 —— 事务带来的额外请求与状态主题;什么时候不该用事务。

3.1 有一个必须亲手做的实验:同一事务里发 3 条,commit 后 read_committed 看到 3 条,abort 后看到 0 条,而 read_uncommitted 全程能看到。这个对照是理解「事务边界」的最短路径。

第二部分:消费治理(第 4–6 章)

如果说第一部分是「怎么写代码」,这一部分就是「怎么让它连续跑三个月不出事」。

第 4 章 消费组工程

  • 4.1 消费组设计:按业务还是按分区 —— 一个组一个职责;同组消费者的处理能力必须一致;多组订阅同一主题的代价。
  • 4.2 分配策略与消费者倾斜 —— range、round-robin、sticky、cooperative-sticky 四种策略;倾斜的成因与观测。
  • 4.3 扩缩容与优雅停机 —— 扩容触发再平衡;停机的正确姿势;max.poll.interval.ms 导致的踢出。

4.2 会把四种 assignor 在同一组分区上跑一遍,把分配结果并排贴出来。分配不均是最容易被忽略的性能问题——分区没坏、消费也没报错,只是有些消费者闲着。

第 5 章 积压

  • 5.1 lag 的三种口径与误读 —— log-end − committed、log-end − current position、含未提交;为什么监控面板上的数字和代码里不一样。
  • 5.2 积压处置:扩容、跳过与降级 —— 判断「能追上」还是「追不上」;临时扩容的天花板;跳过的业务前提。
  • 5.3 消费速率控制与背压 —— pause/resume;自适应用户限速;下游慢时不要硬拉。

5.1 的价值在于消除歧义。同一时刻、同一个分区,三个工具能报出三个不同的 lag 数字,如果不知道口径,就会在事故中误判趋势。

第 6 章 失败处理

  • 6.1 死信队列的设计 —— 什么进死信;死信的元信息(原主题、分区、位移、异常、重试次数);死信的消费与回灌。
  • 6.2 重试主题与退避 —— 多级重试主题;退避时间怎么放;重试的放大效应。
  • 6.3 毒消息隔离与人工干预 —— 毒消息为什么会阻塞整个分区;隔离手段;人工修复后回灌的幂等前提。

6.3 会复现一次「一条消息让整个分区停摆」。这个现象对不熟悉分区模型的人很反直觉:一条消息处理不了,后面的九万条都排在它后面。

第三部分:数据链路(第 7–9 章)

这一部分处理「消息之外」的一致性问题:业务库、事件契约、跨服务补偿。

第 7 章 事务性数据链路

  • 7.1 Outbox 模式 —— 业务表与 outbox 同库同事务;轮询发布;为什么它比「先写库再发消息」可靠。
  • 7.2 CDC:增量快照与变更捕获 —— 日志挖掘与查询轮询的差别;快照与增量如何衔接;op 语义。
  • 7.3 事件溯源入门 —— 事件作为唯一事实来源;重放重建状态;快照;与 outbox 的区别。

7.1 的实验会用内存库完整跑一遍「写库 → 崩溃 → 重启 → 补发」,并给出崩溃恢复后的消息条数与业务表条数完全对齐的证据。7.2 的 Debezium 只做架构对照,正文会明确标注未在本机执行。

第 8 章 事件契约

  • 8.1 领域事件建模 —— 事件是过去时;包含什么、不包含什么;事件 ID 与因果 ID。
  • 8.2 schema 演进与兼容性 —— 加字段、删字段、改类型各自能否兼容;消费者必须容忍未知字段。
  • 8.3 Schema Registry 的取舍 —— 集中式管理的能力与成本;不引入 registry 时怎么自管。

8.2 会做一次真实的兼容性测试:老版本消费者读新版本生产者的消息,四种变更逐个跑,哪些能过、哪些炸,全部用实际反序列化结果说话。

第 9 章 分布式一致性

  • 9.1 Saga:编排与协同 —— 两种 Saga 形态;步骤与补偿;失败在第几步回滚什么。
  • 9.2 补偿事务的幂等 —— 补偿本身也会重复;补偿失败的兜底;补偿与重试的区别。
  • 9.3 最终一致性的度量 —— 一致性延迟怎么测;不一致窗口的告警;对账任务。

9.3 的回答方式是指标而不是承诺:不说「最终会一致」,而是说「P99 一致性延迟 800 毫秒,超过 5 秒的样本占比 0.02%」。

第四部分:顺序、批量与性能(第 10–12 章)

前三部分讨论正确性,这一部分讨论正确性在性能压力下怎么保持。

第 10 章 顺序

  • 10.1 顺序的粒度与代价 —— 全局有序、分区有序、业务键有序各自需要什么;max.in.flight 对顺序的影响。
  • 10.2 热点分区与 key 设计 —— 单调 key 的倾斜;加盐与二次分区;倾斜的观测。
  • 10.3 重分区与历史数据 —— 改分区数会破坏 key→分区映射;历史数据怎么处理;迁移方案。

10.1 会实测 max.in.flight 从 1 调到 5 时出现在途乱序的具体条数。卷一 7.3 节做过一次(在途 5 出现 9 处乱序),本节会补上幂等开启后的对照,说明幂等能消除乱序但会降低吞吐。

第 11 章 消息大小

  • 11.1 消息大小与批量参数 —— max.request.size、batch.size、message.max.bytes 的联动;超过 broker 上限的表现。
  • 11.2 压缩算法选择 —— gzip、snappy、lz4、zstd 的压缩比与 CPU 代价;什么时候压缩是负收益。
  • 11.3 大消息的 claim-check 模式 —— 消息只放指针、内容放对象存储;取回失败的处理;一致性问题。

11.1 的实验是「逐级放大消息直到失败」,把每个上限在哪一层被拦住、报什么错,全部记录下来。

第 12 章 消费端性能

  • 12.1 消费者多线程模型 —— 每线程一个消费者 vs 拉取线程加工作线程;assign 手动分配;顺序如何保持。
  • 12.2 异步提交的陷阱 —— 异步提交失败静默;提交顺序与处理顺序错位;回调里不能做的事。
  • 12.3 消费端性能调优 —— fetch.min.bytes、max.poll.records、fetch.max.wait.ms 的联合调参。

12.2 会复现一次「异步提交丢了位移但没有报错」,这是排查中最难的一类问题:所有日志都是 INFO,数据却少了。

第五部分:治理与验收(第 13–15 章)

最后一部分把前面的东西组织成可以交给团队执行的制度。

第 13 章 平台治理

  • 13.1 多租户与配额 —— 客户端配额(字节速率、请求速率);超配额是降速不是拒绝;配额的影响范围。
  • 13.2 SASL/ACL 与传输加密 —— SASL 机制选择;ACL 的资源粒度;加密的代价;最小权限。
  • 13.3 跨集群复制 —— MirrorMaker2 的模型;异步复制的延迟与重复;主备切换的位移处理。

13.3 会给双集群的完整配置骨架,但本机没有跑通就如实标注,不给编造的延迟数字。

第 14 章 演练

  • 14.1 故障注入:broker 宕机 —— 宕机时生产与消费各自的表现;客户端重试与元数据刷新;恢复后的追赶。
  • 14.2 故障注入:消费端崩溃 —— 崩溃时的位移状态;重启后的重复范围;session.timeout.ms 的作用。
  • 14.3 恢复演练与 RTO/RPO —— RTO 与 RPO 的定义;演练脚本;指标化的恢复时间。

14.2 会用 kill -9 反复杀消费者,统计重复消息的条数分布——这是把「重复范围」从概念变成数字的最直接方式。

第 15 章 验收

  • 15.1 可靠性验收清单 —— 三十条检查项;每条对应哪个实验;不达标怎么改。
  • 15.2 容量与成本核算 —— 可靠性等级的代价;副本数、保留期、跨集群的账。
  • 15.3 libraryd-mq 卷二验收 —— 场景、判据、实测记录、遗留风险。

15.1 的清单设计成可以逐条打分的形式(0 未做 / 1 做了没验证 / 2 验证过),打印出来就是一张检查表。15.3 会把卷二所有实验汇总成验收场景表,并明确指出哪些项本机做不出来。

附录

  • 附录 A 可靠性配置矩阵 —— 生产端、消费端、broker 端三张配置表,每项标注「保什么、代价是什么」。不是参数大全,只收和可靠性有关的项。
  • 附录 B 幂等与事务模板 —— 去重表、状态机、outbox、事务生产者四个可以直接抄走的模板,每个都带可运行的最小示例。

三条阅读路径

路径一:赶时间,只有两小时。 读序、1.1、2.3、15.1。这四篇足够建立责任划分和判据感,遇到具体问题再回来查。

路径二:要落地一条链路。 按 1 → 2 → 4 → 6 → 7 的顺序读。这五章覆盖「发送、消费、失败、补偿」的完整闭环,读完能写出第一条可靠链路。

路径三:准备做演练和验收。 先读 14 章三节,再回头读 5.1、6.3、12.2。演练会暴露问题,先知道问题长什么样,再去读对应章节,效率比顺序读高。

每节末尾的「判据」是什么

卷一的每节末尾常有一条「判据」,卷二延续这个做法,而且更密集。

判据不是「注意事项」,而是一句可以拿去写测试或写监控的断言。例如:

消费处理失败时,位移不得前移;重启后重复条数不超过 in-flight 窗口内的记录数

它不是「建议这样做」,而是「不满足就是错的」。读者可以把判据抄进测试用例,也可以抄进上线检查表。

与卷一、卷三的衔接

从卷一来。 卷一讲了日志、副本、ISR、HW、再平衡、存储与网络。本卷不重复这些机制,但会频繁引用:讲副本不足时回到卷一 4.2,讲再平衡时回到卷一 6.2,讲重建时回到卷一 13.3。

去卷三。 本卷讲的是「一条链路怎么不出错」,卷三讲的是「多条链路怎么组成系统」——CQRS、流处理、拓扑演进、组织边界。本卷的 outbox 与 Saga 是卷三的基础砖块,但本卷不给模式目录。

可以独立读。 没读卷一也能读本卷,需要机制细节时按章节号回查即可。

哪些实验可以跳过

不是所有实验都需要亲手跑。下面这些结论明确、复现代价高的,可以先读结论:

  • 第 13.3 节的跨集群复制(需要两个集群,本机资源不够)。
  • 第 9 章的 Saga 全流程(代码量大,逻辑清楚,可以只读实现)。
  • 第 11.2 节的四种压缩算法对照(与机器相关性强,量级有意义、绝对值没有)。

反过来,下面这些建议一定亲手做,因为它们的结论和直觉相反:

  • 1.2:acks=all 下仍然丢消息(kill 窗口内 ISR 还没收缩)。
  • 4.1 与 4.2:分区数多于消费者数时的分配结果。
  • 6.3:一条毒消息阻塞整个分区。
  • 12.2:异步提交静默丢位移。

一个使用建议

读这一卷,最好手边有一个自己的业务场景。

可靠性没有通用答案。同样是「不丢」,账务系统要靠事务加对账,日志采集系统靠副本加重试,推送系统可能允许丢但要求低延迟。书中的实验给的是方法与代价,具体选哪一档,要对着自己的场景算。

读到某一节觉得「这个代价我付不起」的时候,恰恰是最有价值的时刻——那说明你找到了自己链路的边界。

下一节进入第 1 章,先回答「不丢到底该由谁保证」。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「books」更多文章

  1. 《Java 消息中间件·事件驱动架构》附录 B 架构决策记录模板
  2. 《Java 消息中间件·事件驱动架构》附录 A 术语表
  3. 《Java 消息中间件·事件驱动架构》15.3 全书验收