最近需要设计一个分布式系统,需要一个中间件来存储共享的信息,来保证多个系统之间的数据一致性,调研了两个主流框架Zookeeper和ETCD,发现都能满足我们的系统需求。其中ETCD是K8s中采用的分布式存储,而其底层采用了RAFT算法来保证一致性,所以随便研究了下RAFT算法,这篇文章会从头到尾分析Raft算法的方方面面。
什么是分布式一致性 ?
分布式系统通常由异步网络连接的多个节点构成,每个节点有独立的计算和存储,节点之间通过网络通信进行协作。分布式一致性指多个节点对某一变量的取值达成一致,一旦达成一致,则变量的本次取值即被确定。
在大量客户端并发请求读/写的情况下,维护数据多副本的一致性无疑非常重要,且富有挑战。因此,分布式一致性在我们生产环境中显得尤为重要。
总结来讲,分布式一致性就是为了解决以下两个问题:
- 数据不能存在单个节点(主机)上,否则可能出现单点故障。
- 多个节点(主机)需要保证具有相同的数据。
常见分布式一致性算法
常见的一致性算法包括Paxos算法,Raft算法,ZAB算法等,
- Paxos算法是Lamport宗师提出的一种基于消息传递的分布式一致性算法,使其获得2013年图灵奖。自Paxos问世以来就持续垄断了分布式一致性算法,Paxos这个名词几乎等同于分布式一致性, 很多分布式一致性算法都由Paxos演变而来#
- Paxos是出了名的难懂,而Raft正是为了探索一种更易于理解的一致性算法而产生的。它的首要设计目的就是易于理解,所以在选主的冲突处理等方式上它都选择了非常简单明了的解决方案。
- ZAB 协议全称:Zookeeper Atomic Broadcast(Zookeeper 原子广播协议), 它应该是所有一致性协议中生产环境中应用最多的了。为什么呢?因为他是为 Zookeeper 设计的分布式一致性协议!
本文我们主要介绍Raft算法,后续会对其他算法进行详细介绍。
深入Raft算法
Raft算法和其他分布式一致算法一样,内部采用如下图所示的复制状态机模型,在这个模型中,会利用多台服务器构成一个集群,工作流程如下图所示:
![222e817928393f2cae247cf530337399.jpeg](https://i-blog.csdnimg.cn/blog_migrate/02d2df534a74e57eb0df64814adf32f4.jpeg)
整个工作流程可以归纳为如下几步:
- 用户输入设置指令,比如将设置y为1,然后将y更改为9.
- 集群收到用户指令之后,会将该指令同步到集群中的多台服务器上,这里你可以认为所有的变更操作都会写入到每个服务器的Log文件中。
- 根据Log中的指令序列,集群可以计算出每个变量对应的最新状态,比如y的值为9.
- 用户可以通过算法提供的API来获取到最新的变量状态。
算法会保证变量的状态在整个集群内部是统一的,并且当集群中的部分服务器宕机后,仍然能稳定的对外提供服务。
Raft算法在具体实现中,将分布式一致性问题分解为了Leader选举、日志同步和安全性保证三大子问题,接下来我会对这三方面进行仔细讲解。
Raft算法基础
Raft 正常工作时的流程如下图,也就是正常情况下日志复制的流程。Raft 中使用日志来记录所有操作,所有结点都有自己的日志列表来记录所有请求。算法将机器分成三种角色:Leader、Follower 和 Candidate。正常情况下只存在一个 Leader,其他均为 Follower,所有客户端都与 Leader 进行交互。
![0eae3bfb7d2f0a7c8a8270a6bd1b6e51.jpeg](https://i-blog.csdnimg.cn/blog_migrate/fa9abd5923bb61f60d9a0555b22e3f4e.jpeg)
所有操作采用类似两阶段提交的方式,Leader 在收到来自客户端的请求后并不会执行,只是将其写入自己的日志列表中,然后将该操作发送给所有的 Follower。Follower 在收到请求后也只是写入自己的日志列表中然后回复 Leader,当有超过半数的结点写入后 Leader 才会提交该操作并返回给客户端,同时通知所有其他结点提交该操作。
通过这一流程保证了只要提交过后的操作一定在多数结点上留有记录(在日志列表中),从而保证了该数据不会丢失。
Raft 是一个非拜占庭的一致性算法,即所有通信是正确的而非伪造的。N个结点的情况下(N为奇数)可以最多容忍(N-1)/2个结点故障。如果更多的节点故障,后续的Leader选举和日志同步将无法进行。
子问题1:Leader选举
在了解基本的工作流程之后,首先看下Rsft算法的第一个问题,如何选举Leader。