代码中先构造出一个transport实例,然后将集群中其余节点的访问地址添加到transport中,需通过地址来进行节点之间的互相通信,最后启动一个TCPListener来接收和处理消息。
至此,一个简单的RaftNode就算启动起来了,依据同样的方式再启动其他两个节点,3节点的RaftCluster也运行起来了,不过,只是服务启动成功还远远不够。
数据提交及事件处理
1、 将数据提交到RaftNode,使用Propose方法将数据提交到leader节点
注:只能讲数据Propose到leader节点, 因为只有leader才有能力让follower复制自己的操作。
2、leader节点接收到数据会根据集群的状态判断是否已经能接受数据的提交,leader确定能接收数据后会负责将数据发送到follower。
注:如果是集群节点的改动需要调用ProposeConfChange方法。
3、RaftNode节点数据提交是一个异步的过程,通过Propose方法往RaftNode中提交数据,而RaftNode则在经过一系列的状态判断从另一个线程中通过 Ready()方法通知,此外,集群状态的变化也会通过这个channel来通知,所以当Propose数据之后不知道数据是否提交成功,如果服务的数据有高可用性的要求,这里可能需要进行额外的处理,将异步的提交变成同步的(可以参考swarmkit ProposeValue)。
注:由于数据提交只有一个Propose接口,所以需要对不同的数据进行不同的操作,提前定义好对哪些数据(比如app, cluster)进行什么样的操作(比如Add, Update, Delete),这种情况下就要先在Propose的对象里加上数据和操作之后再进行序列化。
着重注意从 Ready()中收到数据之后的处理,先看代码:
以上代码可以看到最主要的三个处理:
检查RaftCluster的状态是否已经改变,如果该节点已经从follower升级成为leader,需要通知外部的服务这个变化,以便外部服务做出相应的调整
publishEntries,其实就是讲rd。CommittedEntries持久化或者存到相应的地方,可以认为这些CommittedEntries就是已经被RaftCluster接收了的可靠消息。
关于节点状态的变化,需要在外部服务中监听RaftNode的leadershipChange event,由于RaftNode只有在leader上才能Propose数据(相当于写操作),所以cluster中的所有节点地位并不是对等的,比如有的提交数据的功能可能需要等RaftCluster leader election完成后再leader上启动;至于其他的follower节点如果对外想提供和leader一样的服务,则需要自己实现一个proxy,将请求proxy到leader节点, 或者通过grpc来远程调用leader上相应的接口,相关的代码如下所示: