分布式一致性协议
当前业界主流的分布式一致性协议主要有如下几种:
- totem协议(简单即有效)
totem协议,全称是The Totem Single-Ring Ordering and Membership Protocol,是一个基于令牌环的分布式一致性算法。corosync基于totem协议实现。 - paxos协议(二阶段提交)
- raft协议(二阶段提交,基于paxos协议完善和改进)
Raft协议就是Paxos的衍生,etcd基于raft协议实现。 - zab协议(二阶段提交)
ZAB协议是为分布式协调服务Zookeeper专门设计的一种支持崩溃恢复和原子广播协议。
totem协议
totem协议,全称是The Totem Single-Ring Ordering and Membership Protocol,是一个基于令牌环的分布式一致性算法。一个令牌在集群节点之间传递,拿到令牌的节点才能够发消息,消息通过UDP广播发送。所以,totem只适合在局域网里的小集群使用,这种情况下,这个算法性能还算高的。从CAP来说,totem具有强一致性,但几乎没有分区容错性,在网络出现分区时,totem会脑裂,当网络恢复时,会造成消息丢失。
在多个节点组成的集群中,totem实现让一个节点发送消息,其它所有节点都能全部收到,并且有序的提交给上层应用。
说起totem协议,最简单的形象就是,他将多个节点组成一个令牌环。多个节点手拉手形成一个圈,大家依次的传递token。只有获取到token的节点才有发送消息的权利。简单有效的解决了在分布式系统中各个节点的同步问题,因为只有一个节点会在一个时刻发送消息,不会出现冲突。当然,如果有节点发生意外时,令牌环就会断掉,此时大家不能够通信,而是重新组建出一个新的令牌环。
totem的节点有四个状态,也是组建集群的4个阶段。
- Gather阶段:这个阶段用于每个节点向外界广播自己的存在并收集其它节点的存在
- Commit阶段:这个阶段会产生一个代表节点,该节点向其它所有节点收集信息,并将收集的信息传递给其它所有节点,用于后续阶段
- Recovery阶段:这个阶段用于新旧集群交替时,旧集群成员用新集群传递旧集群的消息,使旧集群成员达到所有节点消息全部有序提交到上层
- Operational阶段:这个阶段是集群组建完成正常工作的状态,这个状态一个节点发送的消息其它节点都会全部有序提交给上层
通信方式。
- 当集群有节点要发起通信时,需要等待token。
- 当拿到token后,先广播这次需要发送的数据,然后传递token来确认所有人都接收到消息。
- 如果确认成功,释放token。
节点的加入和退出。
- 当集群中有节点加入时,加入的节点广播一个加入信息,所有人都开始广播自己的信息,当所有人都获得同伴信息,开始由id最小的人提交一个token,交由所有节点确认。
- 如果都确认后,则节点正式加入,开始正常运行。
- 当集群有节点退出时,由于令牌环断链,触发token超时,则同样开始广播信息,然后由最小id提交token,经过确认后恢复正常。
raft协议
Paxos 算法的描述偏学术化,缺失了很多细节,无法直接应用于工程领域。实际工程应用中的分布式算法大多是 Paxos 的变种,Raft协议就是Paxos的简化。
RAFT算法分为两个阶段:Leader选举,日志复制。也有三种角色,分别为:
- Leader(领导者):负责发送要进行共识的数据,如果客户端发送的数据不是发送到Leader而是其他角色,其他角色会进行转发至Leader。
- Follower(追随者):参与共识的角色
- Candidate(候选者):如果Follower没有收到Leader的心跳响应超过150——300ms,会进行Leader选举
正常运行的情况下,会有一个Leader,其他全为Follower,Follower只会响应Leader和Candidate的请求,而客户端的请求则全部由Leader处理,即使有客户端请求了一个Follower也会将请求重定向到Leader。Candidate代表候选人,出现在选举Leader阶段,选举成功后Candidate将会成为新的Leader。
Raft 将一致性问题分解为 3 个独立的子问题:
- Leader 选举Election:Leader 进程失效后能够自动选举出一个新的 Leader
- 日志复制Replication:Leader 保证其他节点的日志与其保持一致
- 状态安全Safety:Leader 保证状态机执行指令的顺序与内容完全一致
leader选举
- 所有节点初始状态为Follower状态,此时没有Leader,肯定会与Leader的心跳超时(一般150——300ms,随机的,这样就是想谁先发出竞选,谁当选leader),此时Candidate就会发出leader竞选给其他节点(大家快选我啊,leader挂掉了);其他节点收到竞选请求,会响应同意,当一个Candidate收到大多数(n/2 + 1)节点的回复,就成为leader。然后与Candidate保持心跳连接。Raft有个Term(任期)的概念,只有在发生Leader选举阶段,term+1,表示新的leader产生,挂掉的节点,或者挂掉的leader重启后,会发现自己的term小于最新的,此时就会切换到日志复制,去同步之前丢失的消息。
- 如果同时有多个Candidate发出竞选,并且都没有获得大多数投票,会一直进行竞选,直到选出leader
日志复制
- leader收到客户端或者其他节点转发过来需要共识的值,会跟随心跳一起广播给其他节点,进行写入
- 其他节点写入后响应成功给leader,当leader收到大多数的follower响应的成功,发出commit命令
- 其他节点收到commit后,进行事务提交,响应成功为leader,leader收到大多数的commit成功,Raft完成
ZAB协议
Zookeeper是一个为分布式应用提供高效且可靠的分布式协调服务。在解决分布式一致性方面,Zookeeper并没有使用Paxos,而是采用了ZAB协议。
ZAB协议基本与Raft相同,都是Multi Paxos的衍生。ZAB与Raft在一些名词的叫法上有区别:如ZAB将某一个Leader的周期称为epoch,而Raft则称之为term。在实现上也有些许不同:Raft为了保证日志连续性,心跳方向为Leader至Follower,ZAB则相反。
消息广播模式
ZAB协议的消息广播过程使用的是一个原子广播协议,类似一个2PC二阶段提交过程。
- Leader将客户端的request转化成一个Proposal(提议);
- Leader为每一个Follower准备了一个FIFO队列,并把Proposal发送到队列上;
- Leader若收到follower的半数以上ACK反馈;
- Leader向所有的follower发送commit。
崩溃恢复
ZAB 定义了 2 个原则:
- ZAB 协议确保那些已经在 Leader 提交的事务最终会被所有服务器提交。
- ZAB 协议确保丢弃那些只在 Leader 提出/复制,但没有提交的事务。
所以,ZAB 设计了下面这样一个选举算法:能够确保提交已经被 Leader 提交的事务,同时丢弃已经被跳过的事务。针对这个要求,如果让 Leader 选举算法能够保证新选举出来的 Leader 服务器拥有集群中所有机器编号(即 ZXID最大)的事务,那么就能够保证这个新选举出来的 Leader 一定具有所有已经提交的提案。