Files
MIT6.824/lecture-06-raft1/6.3-raft-chu-tan.md
2022-01-25 02:41:31 +00:00

5.4 KiB
Raw Permalink Blame History

6.3 Raft 初探

这一部分来初步看一下Raft。

Raft会以库Library的形式存在于服务中。如果你有一个基于Raft的多副本服务那么每个服务的副本将会由两部分组成应用程序代码和Raft库。应用程序代码接收RPC或者其他客户端请求不同节点的Raft库之间相互合作来维护多副本之间的操作同步。

从软件的角度来看一个Raft节点我们可以认为在该节点的上层是应用程序代码。例如对于Lab 3来说这部分应用程序代码就是一个Key-Value数据库。应用程序通常都有状态Raft层会帮助应用程序将其状态拷贝到其他副本节点。对于一个Key-Value数据库而言对应的状态就是Key-Value Table。应用程序往下就是Raft层。所以Key-Value数据库需要对Raft层进行函数调用来传递自己的状态和Raft反馈的信息。

同时如Raft论文中的图2所示Raft本身也会保持状态。对我们而言Raft的状态中最重要的就是Raft会记录操作的日志。

对于一个拥有三个副本的系统来说很明显我们会有三个服务器这三个服务器有完全一样的结构上面是应用程序层下面是Raft层。理想情况下也会有完全相同的数据分别存在于两层应用程序层和Raft层中。除此之外还有一些客户端假设我们有了客户端1C1客户端2C2等等。

客户端就是一些外部程序代码,它们想要使用服务,同时它们不知道,也没有必要知道,它们正在与一个多副本服务交互。从客户端的角度来看,这个服务与一个单点服务没有区别。

客户端会将请求发送给当前Raft集群中的Leader节点对应的应用程序。这里的请求就是应用程序级别的请求例如一个访问Key-Value数据库的请求。这些请求可能是Put也可能是Get。Put请求带了一个Key和一个Value将会更新Key-Value数据库中Key对应的Value而Get向当前服务请求某个Key对应的Value。

所以看起来似乎没有Raft什么事看起来就像是普通的客户端服务端交互。一旦一个Put请求从客户端发送到了服务端对于一个单节点的服务来说应用程序会直接执行这个请求更新Key-Value表之后返回对于这个Put请求的响应。但是对于一个基于Raft的多副本服务就要复杂一些。

假设客户端将请求发送给Raft的Leader节点在服务端程序的内部应用程序只会将来自客户端的请求对应的操作向下发送到Raft层并且告知Raft层请把这个操作提交到多副本的日志Log并在完成时通知我。

之后Raft节点之间相互交互直到过半的Raft节点将这个新的操作加入到它们的日志中也就是说这个操作被过半的Raft节点复制了。

当且仅当Raft的Leader节点知道了所有课程里说的是所有但是这里应该是过半节点的副本都有了这个操作的拷贝之后。Raft的Leader节点中的Raft层会向上发送一个通知到应用程序也就是Key-Value数据库来说明刚刚你提交给我的操作我已经提交给所有同上一个说明副本并且已经成功拷贝给它们了现在你可以真正的执行这个操作了。

所以客户端发送请求给Key-Value数据库这个请求不会立即被执行因为这个请求还没有被拷贝。当且仅当这个请求存在于过半的副本节点中时Raft才会通知Leader节点只有在这个时候Leader才会实际的执行这个请求。对于Put请求来说就是更新Value对于Get请求来说就是读取Value。最终请求返回给客户端这就是一个普通请求的处理过程。

学生提问:问题听不清。。。这里应该是学生在纠正前面对于所有节点和过半节点的混淆

Robert教授这里只需要拷贝到过半服务器即可。为什么不需要拷贝到所有的节点因为我们想构建一个容错系统所以即使某些服务器故障了我们依然期望服务能够继续工作。所以只要过半服务器有了相应的拷贝那么请求就可以提交。

学生提问除了Leader节点其他节点的应用程序层会有什么样的动作

Robert教授哦对抱歉。当一个操作最终在Leader节点被提交之后每个副本节点的Raft层会将相同的操作提交到本地的应用程序层。在本地的应用程序层会将这个操作更新到自己的状态。所以理想情况是所有的副本都将看到相同的操作序列这些操作序列以相同的顺序出现在Raft到应用程序的upcall中之后它们以相同的顺序被本地应用程序应用到本地的状态中。假设操作是确定的比如一个随机数生成操作就不是确定的所有副本节点的状态最终将会是完全一样的。我们图中的Key-Value数据库就是Raft论文中说的状态也就是Key-Value数据库的多个副本最终会保持一致