安全性保证(绝对不会返回一个错误的结果):在非拜占庭错误情况下,包括网络延迟、分区、丢包、冗余和乱序等错误都可以保证正确。
可用性:集群中只要有大多数的机器可运行并且能够相互通信、和客户端通信,就可以保证可用。因此,一个典型的包含 5 个节点的集群可以容忍两个节点的失败。服务器被停止就认为是失败。他们当有稳定的存储的时候可以从状态中恢复回来并重新加入集群。
不依赖时序来保证一致性:物理时钟错误或者极端的消息延迟在可能只有在最坏情况下才会导致可用性问题。
通常情况下,一条指令可以尽可能快的在集群中大多数节点响应一轮远程过程调用时完成。小部分比较慢的节点不会影响系统整体的性能。
Raft使用心跳(heartbeat)触发Leader选举。当服务器启动时,初始化Follower。Leader向所有的Followers周期性的发送heartbeat。如果Follower在选举超时时间内没有收到Leader的heartbeat,就会等待一段随机时间(150ms-300ms)发起一次选举。
Follower先要增加自己的当前任期号,也就把当前的任期号加一并且转换到候选人状态。然后它们会并行的向集群中的其他服务器节点发起请求投票的RPCs来给自己投票。结果会有以下三种情况:
它自己赢得了这次选举
其他的服务器成为了领导者
一段世家之后没有人获取胜利的人。
日志复制Leader被选举出来后.它就开始为客户端提供服务,客户端每个请求都包含一条被复制状态机执行的命令。领导人将这条指令作为新的日志条目附加到日志中去。然后并行的发起附加条目RPCs给其他的服务器,让它们复制这个日志条目,当这条日志条目被安全的复制。领导人会应用这条日志条目到它的状态中然后把执行的结果返回客户端。如果Follower崩溃或者运行缓慢,再或者网络丢包,领导人会不断的尝试附加日志条目RPCs(尽管已经回复了客户端)直到所有Follower都最终存储了所有条目数。
日志由有序编号(log index)的日志组成条目。每个日志条目包含它被创建的任期号(term),和用于状态机执行的命令。如果一个日志条目被复制到大多数服务器上,就被认为可以提交了(commit)了。
Raft维护着一下特征:
1.如果在不同的日志中的两个条目拥有相同的索引和任期号,那么它们存储了相同的指令。
2.如果在不同的日志中的两个条目拥有相同的索引和任期号,那么他们的之前的所有日志条目也全部相同。
第一个特新来这样的一个事实,领导人最多在一个任期里在指定的日志索引位置创建一条日志条目,同时日志条目在日志中的位置也从来不会改变。第二个特性由附加日志RPC的一个简单一致性检查保证。在发送附加日志RPC的时候,领导人会把新的日志条目紧接着之前的条目索引位置和任期号包含在里面。如果跟随者在它的日志中找不到包含相同的日志索引位置和任期号的条目,那么他就会拒绝接收新的条目日志。一致性检查就像一个归纳步骤:一开始空日志状态肯定是满足日志匹配特性的,然后一致性检查保护了日志匹配特性当日志扩展的时候。因此,每当附加日志RPC返回成功时,领导人就知道跟随着的日志时一样的了。
当一个领导人成功当选时,跟随者可能是任何情况(a-f)。每一个盒子表示是一个日志条目,里面的数字表示任期号。跟随者可能缺少一些体制条目(a-b),可能会有一些未被提交的日志条目(c-d),或者两种情况都存在的(e-f)。例如,场景f可能会发生,某些服务器在任期号2的时候是领导人,已附加了一些日志条目到自己的日志中,但在提交之前就就崩溃了,很快这个机器就被重启了,在任期3重新被选为领导人,并且又增加了一些日志条目到自己的日志中,并且又增加了一些日志条目到自己的日志中,在任期2和任期3的日志被提交之前,这个服务器又宕机了,并且在接下来的几个任期里一直处于宕机状态。