《Java 消息中间件·可靠消息与消费治理》序:可靠性是设计出来的,不是配置出来的
为什么 acks=all、手动提交、事务这三张配置牌各自只对了一半:把端到端可靠性拆成生产、broker、消费、下游四段,逐段说明每段能保证什么、在哪里漏、要付什么代价,以及本卷用哪些可复现的实验和判据把每一段钉死,最后给出三卷的分工与贯穿项目 libraryd-mq 的可靠性需求。
tag
为什么 acks=all、手动提交、事务这三张配置牌各自只对了一半:把端到端可靠性拆成生产、broker、消费、下游四段,逐段说明每段能保证什么、在哪里漏、要付什么代价,以及本卷用哪些可复现的实验和判据把每一段钉死,最后给出三卷的分工与贯穿项目 libraryd-mq 的可靠性需求。
《Java 消息中间件·可靠消息与消费治理》全 50 篇:把端到端可靠性拆成生产、broker、消费、下游四段,逐段给出判据、代码与演练记录。覆盖幂等与事务、消费组工程、积压处置、死信与重试、outbox 与 Saga、顺序与消息大小、配额与跨集群、故障演练与验收清单。
把全书用到的可运行件集中成一份模板:单节点 docker run、三节点 compose(每个节点显式写全 listeners,这是本机踩过的坑)、Maven 工程 pom、生产者与消费者最小可运行类、指标打点与优雅关闭。全部按 4.3.0 实跑验证过,复制后改端口即可用。
把全书用到的命令按「集群、主题、消费组、日志、副本、压测」六组整理成一页,每条都标注了它在哪一节被实际用过。另附一节端口与 listener 的实战排查——包括本机遇到的两个坑:宿主机端口被别的进程占用、以及 advertised 端口和映射端口不一致导致客户端连维。
把前十四章的实验汇总成一份可以照着执行的验收清单:十二个场景,每个给出可量化的判据、本卷对应的实测证据和结论,并配一份可以直接改造成脚本的检查骨架。另外单独列出五项本机做不出来、因而不算通过的检查,说明它们会带来什么风险、下一卷在什么条件下能补上。
升级 Kafka 时要同时管住三个版本号:broker 二进制、metadata.version 和客户端 jar。这一节把这三者的关系拆开,给出四组客户端与服务端的交叉实测(含 2.5.1 这种跨越多个大版本的老客户端),并用 KIP-848 新消费组协议演示什么叫「硬下界」,最后给出 KRaft 下的滚动升级顺序与回滚边界。
Kafka 的 MBean 有近千个,真正常看的只有十二个。这一节把它们分成 broker、客户端、业务三层,给出本机实测的 JMX 采集步骤——包括镜像里没有 jcmd、JMX_PORT 会被客户端继承、JmxTool 的包名在 4.x 改过这三个坑,并用一次 5 万条的生产与消费演示数字怎么变、阈值怎么定、告警怎么去抖。
副本落后与副本分歧是两件事:落后会自己追上,分歧不会。本节实跑 kafka-replica-verification.sh 得到 max lag 0,用 offsetLag 与 dump-log 的 crc 逐层取证,再手动截断 follower 的段文件,观察 LEO 从 682 回退到 651 并重建,并给出反直觉结论:offsetLag=0 时段文件字节数可以不同。
LAG 是一个差值,但成因至少三种:生产变快、消费变慢、分区倾斜。本节用限速消费者制造 0 到 26821 的积压,连续采样 `--describe` 的三列,给出「LEO 是否增长、CURRENT-OFFSET 是否推进、LAG 是否均衡」的三步判定法,并实测 max.poll.interval.ms 超时被踢出组与单条处理慢这两种完全不同的表现。
Kafka 的故障不是一张清单,而是一组会互相触发的链条:磁盘满会让 broker 退出,broker 退出会制造 URP,URP 又可能拖垮写入。本节把 URP、离线分区、脑裂、磁盘满、fd 耗尽、ISR 抖动、controller 不可用七类故障做成「现象→指标→命令→根因」对照表,并实跑 URP、fd 耗尽、controller 切换与一次真实的 ENOSPC。
一个 broker 停机一段时间再上线,是接着拉落后的部分,还是把整段日志丢掉重来?本节实测两种情形:日志未截断时 100 MB 追赶约 5 秒;leader 的 log start 越过 follower 之后,follower 丢掉 311 MB 旧段、从 offset 349995 重建。另附 1 MiB/s 限流实测:稳定在 0.96–1.02 MiB/s。
三节点逐个重启,先停 follower 还是先停 leader,差别有多大?本机实测:按 follower→leader 顺序,200 次写入零失败、最长 43ms;按 leader→新 leader 顺序,多了一次选举、一次写入卡了 2.6 秒。而 min.insync.replicas=2 时,再停一台,生产者 26 毫秒内收到 NotEnoughReplicasException。
三个 broker 配了两个机架,三副本的 topic 会怎么放?本节在三节点 KRaft 上实测:每个分区都是 2 个副本落在同一机架、1 个在另一机架,12 个分区无一例外——只要机架里只有一台 broker,机架感知就无法把 3 个副本分到 3 个故障域。文中还统计了跨机架复制链路的真实占比与 broker.rack 的配置边界。
容量规划的输入是业务事件率,不是 Kafka 参数。本节用 libraryd 六个事件主题的假设流量,把一个 Java 程序跑出磁盘、分区、内存、网络四笔账,并实测了 retention.bytes 与 retention.ms 同时设置时谁先触发,以及为什么磁盘占用会超出 retention.bytes 这个「上限」。
acks、batch.size 与 linger.ms、compression.type 三组参数同时拉扯吞吐、延迟与持久性,任何「三个都要」的方案都只是把成本挪到了别处。本节用四组配置各跑两轮压测,给出对照表,并指出这些数字只能看相对关系,以及单节点为什么测不出 acks 的真实代价。
kafka-producer-perf-test.sh 只输出九列,但每一列都可能被误读。本节逐个拆开 num-records、record-size、throughput 与已弃用的 producer-props,贴出三次同参数运行的完整原文,用 94k 与 203k 的差距说明单次数字为什么不能当基准,并划清客户端数字与 broker 真实能力之间的边界。
分区数不是免费的并行度。本节在 1、10、50、200 个分区的 topic 上实测:每个分区稳定占用 1 个文件描述符和 2 个 mmap 区域,controller 元数据日志约 105 字节/分区,启动加载 261 个空日志要 780 毫秒,并解释为什么上千分区会同时拖慢选举、再平衡与尾延迟。
follower 不是被动接收推送,而是主动向 leader 发 fetch。本节用两节点 RF=2 集群实测 acks=1 与 acks=all 的写入速率差,停掉一个 broker 后观察 min.insync.replicas=1 为何仍能写、吞吐为何反而变快,并解释 replica.fetch.* 四个参数各自管什么。
一条 Produce 请求进入 broker 后要经过 acceptor、processor、请求队列和 KafkaRequestHandler 四道关。本节用两节点集群实测线程清单与队列深度:3 个 acceptor、9 个网络线程、16 个处理线程,并解释 mute/unmute 为什么让同一条连接同一时刻只处理一个请求。
容量、文件描述符、IO 线程是三笔独立的账。本节用 JDK 25 跑出 libraryd 三档流量的容量估算表,并在容器内实测 fd:12 个新分区只增加 14 个句柄,删除主题后句柄要 60 秒才释放,而不少环境默认的软限制只有 1024,远不够 3000 分区的集群使用。