kafka14-LEO和HW正常更新案例
我们假设有一个topic,单分区,副本因子是2,即一个Leader副本和一个Follower副本。我们看下 当producer发送一条消息时,broker端的副本到底会发生什么事情以及分区HW是如何被更新的
1 初始状态
初始时Leader和Follower的HW和LEO都是0(严格来说源代码会初始化LEO为-1,不过这不影响之后的讨论)。Leader中的Remote LEO指的就是Leader端保存的Follower LEO,也被初始化成0。此时, 生产者没有发送任何消息给Leader,而Follower已经开始不断地给Leader发送FETCH请求了,但因为 没有数据因此什么都不会发生。值得一提的是,Follower发送过来的FETCH请求因为无数据而暂时会被 寄存到Leader端的purgatory(炼狱)中,待500ms ( replica.fetch.wait.max.ms 参数)超时后会强 制完成。倘若在寄存期间生产者发来数据,则Kafka会自动唤醒该FETCH请求,让Leader继续处理。

2 Follower发送FETCH请求在Leader处理完PRODUCE请求之后
producer给该topic分区发送了一条消息 此时的状态如下图所示:

如上图所示,Leader接收到PRODUCE请求主要做两件事情:
1. 把消息写入Log,同时自动更新Leader自己的LEO
2. 尝试更新Leader HW值。假设此时Follower尚未发送FETCH请求,Leader端保存的Remote LEO依然是0,因此Leader会比较它自己的LEO值和Remote LEO值,发现最小值是0,与当前 HW值相同,故不会更新分区HW值(仍为0)
PRODUCE请求处理完成后各值如下,Leader端的HW值依然是0,而LEO是1,Remote LEO也是 0
| 属性 | 阶段 | 旧值 | 新值 | 说明 |
|---|---|---|---|---|
| Leader LEO | PRODUCE处理完成 | 0 | 1 | 写入了一条数据 |
| Remote LEO | PRODUCE处理完成 | 0 | 0 | 还未Fetch |
| Leader HW | PRODUCE处理完成 | 0 | 0 | min(LeaderLEO=1, RemoteLEO=0)=0 |
| Follower LEO | PRODUCE处理完成 | 0 | 0 | 还未Fetch |
| Follower HW | PRODUCE处理完成 | 0 | 0 | min(LeaderHW=0, FollowerLEO=0)=0 |
3 假设此时follower发送了FETCH请求,则状态变更如下

本例中当follower发送FETCH请求时,Leader端的处理依次是:
1. 读取Log数据
2. 更新remote LEO = 0(为什么是0? 因为此时Follower还没有写入这条消息。Leader如何确 认Follower还未写入呢?这是通过Follower发来的FETCH请求中的Fetch offset来确定的)
3. 尝试更新分区HW:此时Leader LEO = 1,Remote LEO = 0,故分区HW值= min(Leader LEO, Follower Remote LEO) = 0
4. 把数据和当前分区HW值(依然是0)发送给Follower副本
而Follower副本接收到FETCH Response后依次执行下列操作:
- 写入本地Log,同时更新Follower自己管理的 LEO为1
- 更新Follower HW:比较本地LEO和 FETCH Response 中的当前Leader HW值,取较小者, Follower HW = 0
此时,第一轮FETCH RPC结束,我们会发现虽然Leader和Follower都已经在Log中保存了这条消 息,但分区HW值尚未被更新,仍为0
| 属性 | 阶段 | 旧值 | 新值 | 说明 |
|---|---|---|---|---|
| Leader LEO | PRODUCE和Follower FETCH 处理完成 | 0 | 1 | 写入了一条数据 |
| Remote LEO | PRODUCE和Follower FETCH 处理完成 | 0 | 0 | 第一次fetch中offset为0 |
| Leader HW | PRODUCE和Follower FETCH 处理完成 | 0 | 0 | min(LeaderLEO=1, RemoteLEO=0)=0 |
| Follower LEO | PRODUCE和Follower FETCH 处理完成 | 0 | 1 | 同步了一条数据 |
| Follower HW | PRODUCE和Follower FETCH 处理完成 | 0 | 0 | min(LeaderHW=0, FollowerLEO=0)=0 |
4 Follower第二轮FETCH

Follower发来了第二轮FETCH请求,Leader端接收到后仍然会依次执行下列操作:
- 读取Log数据
- 更新Remote LEO = 1(这次为什么是1了? 因为这轮FETCH RPC携带的fetch offset是1,那 么为什么这轮携带的就是1了呢,因为上一轮结束后Follower LEO被更新为1了)
- 尝试更新分区HW:此时leader LEO = 1,Remote LEO = 1,故分区HW值= min(Leader LEO, Follower Remote LEO) = 1
- 把数据(实际上没有数据)和当前分区HW值(已更新为1)发送给Follower副本作为 Response
同样地,Follower副本接收到FETCH response后依次执行下列操作:
- 写入本地Log,当然没东西可写,Follower LEO也不会变化,依然是1
- 更新Follower HW:比较本地LEO和当前LeaderHW取小者。由于都是1,故更新follower HW = 1
| 属性 | 阶段 | 旧值 | 新值 | 说明 |
|---|---|---|---|---|
| Leader LEO | 第二次Follower FETCH处理完成 | 1 | 1 | 未写入新数据 |
| Remote LEO | 第二次Follower FETCH处理完成 | 0 | 1 | 第二次fetch中offset为1 |
| Leader HW | 第二次Follower FETCH处理完成 | 0 | 1 | min(LeaderLEO=1, RemoteLEO=0)=0 |
| Follower LEO | 第二次Follower FETCH处理完成 | 1 | 1 | 未写入新数据 |
| Follower HW | 第二次Follower FETCH处理完成 | 0 | 1 | min(LeaderHW=0, FollowerLEO=0)=0 |
此时消息已经成功地被复制到Leader和Follower的Log中且分区HW是1,表明消费者能够消费 offset = 0的消息
浙公网安备 33010602011771号