Ozone内部使用的RaftLeader/RaftFollower的一致性同步机理

前言


在之前的文章中,笔者提过Ozone内部使用的是基于Raft协议的一致性控制,以此保证Ozone Container操作请求在Pipeline节点上的一致性。但是这里面具体是怎么样的一个过程呢?RaftLeader/RaftFollower的一致性同步这块其实并不是实现在Ozone这个项目里,而是在外部使用库Apache Ratis里的。本文笔者来聊聊Apache Ratis中RaftLeader、RaftFollower是如何做一致性的控制的。

Apache Ratis基于Raft协议的一致性同步过程


在之前的文章Apache Ratis的Ratis Server主从同步机制里,笔者简单阐述过其中的主要角色服务和原理过程,过程图如下所示:
在这里插入图片描述
这里再次引用下前文对于上述服务角色的介绍:

RetryCache,对于客户端而言,Ratis Sever内部有RetryCache,为了避免客户端发起重复的请求操作。
Raft Log,Raft Log可以理解为是Ratis Server的WAL,每次用户的操作会被转化为transaction log然后被写入到Raft Log中,如果log写满了,则会新创建Raft Log文件进行写入。
LogAppender,此角色服务是将Leader内最新的transaction log发送到远端的各个Follower服务中,并获取返回结果。然后及时更新Raft Log的commit index。
StateMachineUpdater,此角色根据Raft Log当前的commit记录,及时apply 最新committed的log entry到StateMachine中,然后purge掉commit index之前的log文件。同时它会定期给StateMachine做snapshot操作。StateMachineUpdater的功能有点类似于Checkpoint的角色功能。

不过上面提到的部分粒度比较粗,有很多细节部分没有提到,比如RaftLeader/RaftFolloer如何认定本地的RaftLog是已经Commit的,又比如说RaftLog和StateMachine间是如何进行交互的等等。下面我们对这里面的细节内容做更进一步的分析。

Apache Ratis内部RaftLeader/RaftFollower过程调用分析


在本小节的过程调用分析中,我们还是同样会有上面提到的角色服务:

  • RaftLeader/RaftFollower
  • RaftLog
  • StateMachineUpdater
  • StateMachine

在整个RaftLeader/RaftFollower的同步过程中,总共可以拆分为3个阶段:

第一个阶段,RaftLeader的本地写Raft log操作。RaftLeader本地接受到客户端的操作请求,然后触发写本地Raft log操作。在此过程中,StateMachine会进行请求transaction的验证操作,验证通过,允许RaftLeader执行写log entry的操作。RaftLeader在写log entry时,如果log entry附有额外数据,此部分数据将会被额外写入StateMachine内。Write log entry如若达到Raft log flush阈值条件时,则会进行一次flush到外部持久存储的操作,同时触发StateMachine的flushStateMachineData的操作。OK到此为止,此过程中新写入的log还没有被Committed。

第二个阶段,RaftLeader发送append log的请求到RaftFollower上。在此过程中,将会进行如下步骤:

RaftLeader构造append log的请求实例,此过程将会从本地log中读取log entry数据,如果此log entry数据还带有StateMachine data的时候,还需要从StateMachine中读取相应的数据。

RaftLeader发送append log的请求到各个Follower上,Follower接受到请求后,首先更新自身log的Commit Index为Leader的Commit Index。然后通知本地StateMachineUpdater去apply到最新Commit Index的Raft log。Follower倘若发现接受到的新Raft log和本地不一致时(在重新选举产生新Leader时,会有可能导致这种请求发生),则进行本地log的truncate操作。如果一致的话,则进行后续本地log entry的写操作,此过程和第一阶段RaftLeader写本地log的过程类似,也会有StateMachine的相关触发操作。

这个过程还没有结束,Follower在执行完本地的log append的操作后,会返回RaftLeader回复结果,附有Follow本地最新的log信息,包括已经写出的Index和下一个Index。RaftLeader收到后,判断是否半数以上已经成功接受到新append的log entry,则进行Commit Index的更新,更新到上次append成功到Follower的那个Index位置上。

第三个阶段,RaftLeader通知本地的StateMachineUpdater去apply最新的Raft log到StateMachine中。这个过程RaftLeader和Follower的逻辑是一致的。

在上述过程里,我们可以看到,RaftLeader是判断RaftLog是否可以标记为Commit状态的确定者,确定的标准为它本地新写的Raft log是否已经被半数以上的Follower接受到并写出到了它们的Raft log内,则进行Leader本地的Commit Index更新。而Follower的Commit则取决于Leader发来的最新的Commit Index。换句话说,Follower只要等RaftLeader告诉我可以更新哪个最新的Commit Index了,然后我就apply最新的log entry到StateMachine中去

上述的交互过程如下流程图所示(同一个颜色过程表示属于同一阶段过程):
在这里插入图片描述

发布了388 篇原创文章 · 获赞 424 · 访问量 207万+

猜你喜欢

转载自blog.csdn.net/Androidlushangderen/article/details/104440537