|NO.Z.00077|——————————|BigDataEnd|——|Hadoop&kafka.V62|——|kafka.v62|稳定性|一致性保证.v02|
一、HW和LEO正常更新案例
### --- HW和LEO正常更新案例
~~~ 我们假设有一个topic,单分区,副本因子是2,即一个Leader副本和一个Follower副本。
~~~ 我们看下当producer发送一条消息时broker端的副本到底会发生什么事情以及分区HW是如何被更新的。
### --- 初始状态
~~~ 初始时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继续处理。

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

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

~~~ # 假设此时follower发送了FETCH请求,则状态变更如下:
~~~ # 本例中当follower发送FETCH请求时,Leader端的处理依次是:
~~~ 读取Log数据
~~~ 更新remote LEO = 0(为什么是0? 因为此时Follower还没有写入这条消息。
~~~ Leader如何确认Follower还未写入呢?这是通过Follower发来的FETCH请求中的Fetch offset来确定的)
~~~ 尝试更新分区HW:此时Leader LEO = 1,Remote LEO = 0,
~~~ 故分区HW值= min(LeaderLEO, Follower Remote LEO) = 0
~~~ 把数据和当前分区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。
属性 | 阶段 | 旧值 | 新值 | 备注 |
LeaderLEO | PRODUCE和Follower FETCH 处理完成 | 0 | 1 | 写入一条数据 |
RemoteLEO | PRODUCE和Follower FETCH 处理完成 | 0 | 0 | 第一次fetch中offset为0 |
LeaderHW | PRODUCE和Follower FETCH 处理完成 | 0 | 0 |
min(LeaderLEO=1,
RemoteLEO=0)=0 |
FollowerLEO | PRODUCE和Follower FETCH 处理完成 | 0 | 1 | 同步了一条数据 |
FollowerHW | PRODUCE和Follower FETCH 处理完成 | 0 | 0 |
min(LeaderHW=0,
FollowerLEO=1)=0 |
### --- Follower第二轮FETCH
~~~ 分区HW是在第二轮FETCH RPC中被更新的,如下图所示:

### --- 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和Follower的Log中且分区HW是1,
~~~ 表明消费者能够消费offset = 0的消息。
属性 | 阶段 | 旧值 | 新值 | 备注 |
LeaderLEO | PRODUCE和Follower FETCH 处理完成 | 1 | 1 | 未写入新数据 |
RemoteLEO | PRODUCE和Follower FETCH 处理完成 | 0 | 1 | 第2次fetch中offset为1 |
LeaderHW | PRODUCE和Follower FETCH 处理完成 | 0 | 1 | min(RemoteLEO,LeaderLEO)=1 |
FollowerLEO | PRODUCE和Follower FETCH 处理完成 | 1 | 1 | 未写入新数据 |
FollowerHW | PRODUCE和Follower FETCH 处理完成 | 0 | 1 |
第2次fetch resp中的LeaderHW和本地FollowerLEO都是1
|
### --- FETCH请求保存在purgatory中,PRODUCE请求到来。
~~~ 当Leader无法立即满足FECTH返回要求的时候(比如没有数据),
~~~ 那么该FETCH请求被暂存到Leader端的purgatory中(炼狱),待时机成熟尝试再次处理。
~~~ Kafka不会无限期缓存,默认有个超时时间(500ms),一旦超时时间已过,则这个请求会被强制完成。
~~~ 当寄存期间还没超时,生产者发送PRODUCE请求从而使之满足了条件以致被唤醒。
~~~ # 此时,Leader端处理流程如下:
~~~ Leader写Log(自动更新Leader LEO)
~~~ 尝试唤醒在purgatory中寄存的FETCH请求
~~~ 尝试更新分区HW
Walter Savage Landor:strove with none,for none was worth my strife.Nature I loved and, next to Nature, Art:I warm'd both hands before the fire of life.It sinks, and I am ready to depart
——W.S.Landor
分类:
bdv013-kafka
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通