因此该候选者就成为了Leader领导人,它可以向选民也就是Follower们发出指令,比如进行日志复制;
如果一旦Leader宕机崩溃了,那么Follower中会有一个成为候选者,发出选举邀请;
Follower同意后,其成为Leader;
选出leader之后,所有的操作都需要在leader上进行,leader把所有的操作下发到集群中的其他服务器(follower),follower收到消息,完成操作commit之后需要向leader汇报状态;如果leader处于不可用的状态,则需要重新进行选举。
为了与容错方式达成一致性,Raft不要求所有的服务器100%达成一致,只要超过半数的服务器达成一致就可以了,假设有N台服务器,N/2+1超过半数,也就是说一个3节点的Raft集群允许一个节点宕机,一个5节点的Raft集群可以允许2个节点宕机,所以为了更有效的利用服务器,一般Raft集群里服务器的数量都是奇数,建议配置运行3或5节点的Raft集群, 最大限度的提高可用性, 而且不会牺牲很多性能。
三、实践:如何自己动手写一个内置Raft集群的分布式服务
下面结合swan中的具体代码,介绍如何启动RaftNode,以及处理数据和有关Raft状态的相关实践,swan的分布式存储的设计草图如下:
如何启动一个RaftNode
先分享下如何自己动手写一个内置Raft集群的分布式服务,由于是使用Go语言开发,所以选用etcd/raft。
在Raft集群中最重要的概念就是一个RaftNode,多个互相连通的RaftNode组成了一个RaftCluster,可以通过以下代码快速启动/重启一个RaftNode。
该方法是在RaftNode启动时已经知道集群未来的规模,将集群的其他节点ID写入到配置中,如果要实现节点的Auto Join,则需要在Start的Peers参数处传入空值;另外,如果服务之前已经运行了一段时间,在启动服务时就需要从WAL中读取上一次服务停止时的状态和数据,然后在这些数据的基础上继续运行。
RaftNode启动成功后,各个节点之间的通信需要借助一个Transport,同样还是使用etcd提供的httptransport ,当然,也可以基于grpc实现所有的通信方法(参考swarmkit的实现),以下是启动transport的代码实现。