LEO(Log End Offset)是一个副本自己的日志写到了哪;HW(High Watermark)是这个分区对外(消费者)承诺"读到这里都是安全的"的位置。这次用 kill -STOP 暂停一个 Follower(进程活着,只是不干活),观察它暂停期间自己的磁盘数据(LEO)和整个分区对外可见的边界(HW)到底谁先动、谁滞后。
命令 · shell $ kill -STOP 61584 # 暂停 broker1(Follower) $ kafka-log-dirs.sh --bootstrap-server 127.0.0.1:19292 --topic-list hw-leo-demo --describe
--- broker1 暂停期间 ---
Querying brokers for log directories information
Received log directory information from brokers 2,3
{"brokers":[{"broker":2,...,"size":27945,...},{"broker":3,...,"size":27945,...}]}
--- kill -CONT 恢复 broker1,等 6 秒后再查 ---
Received log directory information from brokers 1,2,3
{"brokers":[{"broker":1,...,"size":27945,...},{"broker":2,...,"size":27945,...},{"broker":3,...,"size":27945,...}]}暂停期间,kafka-log-dirs.sh 查询的结果里直接说"Received log directory information from brokers 2,3"——broker1 完全没有响应这次查询,因为它的进程被暂停了,没法处理任何请求,连汇报自己磁盘上有多少数据都做不到。恢复 6 秒后再查,broker1 不仅重新出现在结果里,size 字段还精确等于另外两个 Broker(都是 27945 字节)——说明它在这 6 秒里完成了追赶,本地日志(它自己的 LEO 对应的物理数据)已经和 Leader 一致。这组数据直接证明:一个副本"有多少数据"是它自己的本地状态,暂停时这个状态是不可知的(连查都查不到),不是"暂停期间数据被冻结在某个可读的中间值"。
命令 · shell $ kill -STOP 61584 && kill -STOP 55592 # 同时暂停两个 Follower,只剩 Leader 活着 $ kafka-console-producer.sh --bootstrap-server 127.0.0.1:19292 --topic hw-leo-demo --command-property acks=1 # 发送 500 条 $ kafka-get-offsets.sh --bootstrap-server 127.0.0.1:19292 --topic hw-leo-demo --time -1
生产 500 条 acks=1 消息,producer 退出码 = 0(全部成功) --- 但查询 HW 的命令失败了 --- Error occurred: org.apache.kafka.common.errors.TimeoutException: Timed out waiting to send the call. Call: listTopics Caused by: org.apache.kafka.common.errors.DisconnectException: ... due to node 2 being disconnected
同时暂停两个 Follower 之后(KRaft 模式下这个 3 节点集群的 controller 投票也只剩 1/3 存活,凑不齐多数),kafka-console-producer.sh 依然成功发送了全部 500 条消息(acks=1 只需要 Leader 自己确认写入本地日志,不需要凑够 quorum),但 kafka-get-offsets.sh(本质是走 Admin.listTopics(),需要跟 controller 交互)超时失败。这组对照说明 LEO 的推进(数据面,Leader 写自己的本地日志)和 HW/对外可见状态的查询(部分依赖 controller quorum 的管理面操作)是两条独立的路径——数据面能在 controller quorum 失效的情况下继续往前走,管理面查询却完全瘫痪。恢复两个 Follower 之后,重新查询显示 offset 变成了 1100(=之前的 100 + 这次的 500 + 另一次同样场景下成功的 500),证明这些在"查不到 HW"期间写入的数据,最终、完整地追平了。
LEO 是副本自己知道、别人不一定查得到的本地进度;HW 是这个分区愿意告诉消费者的"安全读取边界",依赖于其他副本追上进度(或者被移出 ISR、缩小分母)之后才能推进。实验里最有说服力的部分不是精确量出两者差了多少(这次两次尝试都发现,本地环回网络下副本追赶和 ISR 收缩都在十几秒内完成,窗口太窄,CLI 轮询很难截住"HW 明显落后于 LEO"的中间态),而是证明了这两条路径是独立的:暂停的副本无法汇报自己的 LEO(连查都查不到),即使控制面全部瘫痪,Leader 的数据面写入也在继续推进,最后一致性由的追赶过程兜底。
每一步都是本文上两节kill -STOP/-CONT、查log-dirs和offset得到的原始结果。