今天的文章,咱们会通过图的方式,来深入学习和理解分布式一致性的实现原理。


开始的时候,咱们先来灵魂一问:什么是分布式一致性?


你的应用是单节点吗?

你的系统用户多吗、支持扩容吗?

你的系统扩容后数据能保持一致吗?

你的系统是否使用Raft、Paxos?

……



是否理解都没关系,后面开始咱们的例子,通过图的方式,来描述一致性的工作原理。


一、前奏


假设咱们有个系统。是个单节点的系统,只部署在一个实例上。可以把它理解成一个数据库服务(Database Server),实例上只有一个数据X,咱们后续的故事都是要围绕操作变更X的值开展的。


图解分布式一致性算法_java




咱们还有个客户端(Client),它要向节点 (Server) 写数据,这个时候应用代码写起来也简单,这个数据的一致性很容易保证。 

图解分布式一致性算法_java_02




图解分布式一致性算法_java_03


写请求执行后,客户端和服务端的X数据都变成了8。


在用户量少的时候,还天天操心啥时候来个百万千万用户,等用户稍微一多起来才发现,原来的一个实例扛不住了。这个时候为了支持更多用户访问,又扩容出来了更多的实例。


图解分布式一致性算法_java_04

那么这下问题来了,当我们有多个实例节点间的时候,客户端向其中一个节点写了数据,如何再同步给其他节点呢?怎样保证节点间数据的一致性呢?这就是分布式一致性问题


二、 Raft 协议概览


Raft 就是一个解决上述分布式一致性问题的协议。类似的协议还有Paxos、Zab等等。相比 Paxos,Raft 更易理解和实现。


咱们本次会俯瞰 Raft , 来了解其工作原理。


在 Raft 里, 节点可能存在三种状态:


  • Follower

  • Candidate

  • Leader




在后面的图里内容中,上述三种状态分别和下面三个图一一对应。

图解分布式一致性算法_java_05     Follower


图解分布式一致性算法_java_06     Candidate


图解分布式一致性算法_java_07    Leader


任何时候,都只会处于上述三种状态中的一种。初始时候,所以节点都处于 Follower状态。


图解分布式一致性算法_java_08


如果follower 节点不再能接收到 leader 节点的消息, 他们的状态就会变成 Candidate。


图解分布式一致性算法_java_09


Candidate 节点会向其他节点发起投票请求,

图解分布式一致性算法_java_10


其他节点也会投票进行响应。

图解分布式一致性算法_java_11


如果获得了多数节点的投票,那 Candidate节点会成为Leader。

图解分布式一致性算法_java_12

上面的这个过程就是分布式一致性协议中的「选举」(Leader Election)。


后续所有对于系统的变更,都是通过Leader进行的。经由 Leader 再到达其他节点


图解分布式一致性算法_java_13

每次 Client 的变更请求到达 leader 时,都会视为一个 Entry ,先添加到节点的日志 (Log)里。这个新增的 log entry,目前还并没有提交,所以不会真的更新节点里 X 的值。


图解分布式一致性算法_java_14

leader 首先会复制 log entry 给所有 follower节点。


图解分布式一致性算法_java_15


然后, leader 会等待,直到多数节点都写入了entry。

图解分布式一致性算法_java_16

在收到多数节点的写入响应之后,  leader 节点会将 entry 提交,此时 leader节点的值变成了 5. 


图解分布式一致性算法_java_17


然后  leader 会通知 follower 节点 自己的 entry 已经提交了。 

图解分布式一致性算法_java_18


  此时各个 follower 节点也会将之前收到的entry 进行提交


整个系统目前所有的集群节点都达到了一致的状态。这个过程一般称为日志复制(Log Replication)。


三、 Leader Election


Raft 里有项控制选举的超时时间设置。选举超时时间是一个follower变成 candidate 的等待时间。值是150ms到300ms间的一个随机值.

图解分布式一致性算法_java_19


在选举超时后, follower 会变成 candidate 开始一个新的选举周期(term),同时会给自己投一票。

图解分布式一致性算法_java_20

并且给其他节点发送请求投票的消息。

图解分布式一致性算法_java_21


如果收到消息的节点在这一个周期还没有投过票,那他需要给 candidate 投一票,并且该节点重置他的选举超时时间。

图解分布式一致性算法_java_22


如果 candidate 收到大多数节点的投票,就会成为leader。


图解分布式一致性算法_java_23

 

leader 此时会在心跳检测的周期内,给给所有的follower发送Append Entries messages 。检测的频率是通过心跳超时时间设置的。


图解分布式一致性算法_java_24

然后 每个 follower 也会给 Append Entries message 发送响应。


图解分布式一致性算法_java_25

这个选举周期会一直持续直到某个 follower 停止心跳消息接收变成 candidate。



我们停掉 leader 再来看一下重新选举的情况。


此时 节点 B 已经停止,所以 节点A 和节点C 都收不到心跳消息。咱们前面说收不到心跳消息的话,节点的状态会从 Follower 变成 Candidate,所以A 和C 在各自的选举超时时间设置内会改变状态,



图解分布式一致性算法_java_26


此时,节点 A 和 节点 C 都在等待,由于节点C先超时,所以会先开始一轮选举。和上面的选举过程一样,会先新增Term,给自己投一票,并发送投票请求给其他节点。

在收到节点A的投票后,节点C现在升级为 term 2的 leader。

图解分布式一致性算法_java_27


咱们前面总在说的「大多数节点投票」(amajority of votes)。这个能保证能在一个投票周期内,只会产生一个 leader。


假设两个节点同时从 Follower 状态变成了 Candidate,这个时候两者都会升级 Term,并请求其他节点不给自己投票。

图解分布式一致性算法_java_28

图解分布式一致性算法_java_29

这个时候两者的 Term 其实是相同的,在同一个 Term 内 其他节点只会投出一票,所以每个 Candidate 都只会收到一个节点的投票。

图解分布式一致性算法_java_30

图解分布式一致性算法_java_31

 因为没有超过「大多数」,所以都会不能成为 leader。这些节点就会等待新一轮的选举,此时节点D先开始发起投票,并收到了大多数的投票,所以在Term 5 最终成为 leader。

图解分布式一致性算法_java_32


四、 Log Replication


在选举出一个leader之后,就需要复制所有的变更信息到系统里的所有节点。这些是通过和心跳检测相同的的Append Entries message 来完成的。

图解分布式一致性算法_java_33



我们来看下这个过程。一开始, client会向leader发送一个变更。

图解分布式一致性算法_java_34

这个变更会加到 leader的log中,并会在下一次心跳检测的时候发给 follower。

图解分布式一致性算法_java_35

follower在收到消息之会,都会给 leader 发送响应的 ack消息。

图解分布式一致性算法_java_36

收到大多数的 follower 响应之后, leader 会提交这个entry,并且发送响应给 client。

图解分布式一致性算法_java_37

图解分布式一致性算法_java_38

并在下次心跳的时候,通知 follower 们执行提交操作。

图解分布式一致性算法_java_39


follower 在写入完成后再给 leader 发送响应。

图解分布式一致性算法_java_40

此时 Client 又给给 leader 发送消息,要给X 来执行个 加2 的操作。leader 将消息添加到 log 之后,给各个follower 发送心跳。



图解分布式一致性算法_java_41


follower 们收到之后继续返回响应。

图解分布式一致性算法_java_42


leader 收到 ack 之后,确认本次执行进行 commit ,然后给 client 发回响应。并在下次心跳的时候,把写入发给各个 follower。

图解分布式一致性算法_java_43


此时,咱们的系统里 X变成了7,各个节点的数据保持一致。


图解分布式一致性算法_java_44



五、「大多数」是多少?


前面许多场景咱们都提到「大多数」。那大多数到底是多少呢?

比如我们在选举的时候,在日志复制的时候,都需要大多数 follower 发送回响应消息。

这里的大多数和咱们日常生活里的基本一致,即超过半数。比如一共有5个节点,此时有一个 candidate 起成为 leader,在投票过程中,至少要收到3个投票才行。



官网有个可以自定义时间进行交互的动图,感兴趣的朋友可以自行查看。

图解分布式一致性算法_java_41