zookeeper、Paxos算法、ZAB协议、CAP理论介绍

分布式协调服务器Zokeeper

  • Zookeeper概述

1.1 Zookeeper简介

Zookeeper是一个开源的分布式应用程序协调服务器,其为分布式系统提供一致性服务。其一致性是通过基于Paxos算法的ZAB协议完成的。其主要功能包括:配置维护、域名服务、分布式同步、集群管理等。

1.1.1功能简介

(1)配置维护

分布式系统中,很多服务都是部署在集群中的,即很多台服务器中部署着完全相同的应用,起着完全相同的作用。当然,集群中的这些服务器的配置文件是完全相同的。

若集群中服务器的配置文件需要进行修改,那么我们就需要逐台修改这些服务器中的配置文件。如果我们集群服务器比较少,那么这些修改还不是太麻烦,但如果集群服务器特别多,比如某些大型互联网公司的Hadoop集群有数千台服务器,那么纯手工的更改这些配置文件几乎就是一件不可能完成的任务。即使使用大量人力进行修改,但是过多人员参与,出错的概率大大提升,对于集群的形成的危险是很大的。

这时候Zookeeper就可以派上用场了。其对于配置文件的维护采用的是发布/订阅模型。发布者将修改好的集群的配置文件发布到Zookeeper服务器的文件系统中,那么订阅者马上就可以接收到通知,并主动的去同步Zookeeper里的配置文件。Zookeeper具有同步操作的原子性,确保每个集群服务器的配置文件都能被正确的更新。

(2)域名服务

在分布式应用中,一个项目包含多个工程,有些工程是专门为其他工程提供服务的。一个项目中可能会存在多种提供不同服务的功能,而一种服务又可能存在多个提供者(服务器)。所以,用于消费这些服务的客户端工程若要消费这些服务器,就变得异常的复杂了。

此时,Zookeeper就可以上场了。为每个服务起一个名称,将这些服务的名称与提供这些服务的主机地址注册到Zookeeper中,形成一个服务映射表。服务消费者只需要通过服务名称即可享受到服务,而无需了解服务具体的提供者是谁。服务的减少、添加、变更,只需修改Zookeeper中的服务映射表即可。

阿里的Dubbo就是使用Zookeeper作为服务域名服务器。

zookeeper、Paxos算法、ZAB协议、CAP理论介绍_第1张图片

(3)分布式同步

在分布式系统中,很多运算(对请求的处理)过程是由分布式集群中的若干服务共同计算完成的,并且他们之间的运算还具有逻辑上的先后顺序。如何保证这些服务器运行期间的同步性呢?

使用Zookeeper可以协调这些服务器间运算的过程。让这些服务器都同时监听Zookeeper上的的同一个znode(Zookeeper文件系统中的一个数据存储节点),一旦其中一个服务器update了znode,那么另一个相应服务器能够收到通知,并作出相应处理。

(4)集群管理

集群管理中最麻烦的就是节点故障管理。Zookeeper可以让集群选出一个健康的节点作为Master,Master随时监控着当前集群中的每个节点的健康状况,一旦某个节点发生故障,Master会把这个情况立即通知给集群中的其他节点,使其他节点对于任务的分配作出相应的调整。Zookeeper不仅可以发现故障,也会对故障进行甄别,如果该故障可以修复,Zookeeper可以自动修复,若不能修复则会告诉系统管理员错误的原因让管理员迅速定位问题。

但这里也有个问题:Master故障了,那怎么办?Zookeeper内部有一个选举算法,当Master故障出现时,Zookeeper能马上选出新的Master对集群进行管理。

1.1.2 一致性要求

什么是zk的一致性呢?其需要满足以下几点要求:

(1)顺序一致性

同一个客户端发起的n多个事务请求(写请求),最终将会严格按照其发起顺序被应用到Zookeeper中。

(2)原子性

所有事务请求的结果在集群中所有机器上的应用情况是一致的。也就是说要么整个集群所有主机都成功应用了某一个事务,要么都没有应用,不会出现集群中部分主机应用了该事务,而另外一部分没有应用到的情况。

(3)单一视图

无论客户端连接的是哪个Zookeeper服务器,其看到的服务端数据模型都是一致的。

(4)可靠性

一旦服务端成功的应用了一个事务(写操作),并完成了对客户端的响应,那么该事务所引起的服务端状态变更将会一直被保留下来,除非有另一个事务又对其进行了变更。

(5)实时性

通常人们看到实时性的第一反应是,一旦一个事务被成功应用,那么客户端能够立即从服务端上读取到这个事务变更后的最新数据状态。这里需要注意的是,Zookeeper仅仅保证在一定的时间段内,客户端最终一定能够从服务器上读取到最新的数据状态。

1.2 Zookeeper中的重要概念

1.1.1 Session

Session是指客户端会话。Zookeeper对外的服务端口默认是2181,客户端启动时,首先会与zk服务器建立一个TCP长连接,从第一次连接建立开始,客户端会话的生命周期也开始了。通过这个长连接,客户端能够通过心跳检测保持与服务器的有效会话,也能够向Zookeeper服务器发送请求并接受响应,同时还能通过该连接接收来自服务器的Watcher事件通知。

Session的SessionTimeout值用来设置一个客户端会话的超时时间。当由于服务器压力太大、网络故障或是客户端主动断开链接等各种原因导致客户端连接断开时,只要在SessionTimeout规定的时间内客户端能够重新连接上集群中任意一台服务器,那么之前创建的会话仍然有效。

1.2.2 znode

zookeeper、Paxos算法、ZAB协议、CAP理论介绍_第2张图片

Zookeeper的文件系统采用树形层次化的目录结构,与unix文件系统非常相似。每个目录在zookeeper中叫做一个znode,每个znode拥有一个唯一的路径标识,即名称。Znode可以包含数据和子node(临时节点不能有子node)。Znode中的数据可以有多个版本,所以查询某路径下的数据需带上版本号。客户端应用可以在znode上设置监视器(watcher)。

1.2.3 watcher机制

Zk通过watcher机制实现了发布/订阅模式。Zk提供了分布式数据的发布/订阅功能,一个发布者能够让多个订阅者同时监听某一主题对象,当这个主题对象状态发生变化时,会通知所有订阅者,使他们能够做出相应的处理。Zk引入了watcher机制来实现这种分布式的通知功能。Zk允许客户端(订阅者)向服务端(发布者)注册一个watcher监听,当服务端的一些指定事件触发这个watcher,那么就会向指定客户端发送一个事件通知。而这个事件通知则是通过tcp长连接的session完成的。

1.2.4 ACL

ACL全称为Access Control List(访问控制列表),用于控制资源的访问权限,是zk数据安全的保障。Zk利用ACL策略控制znode节点的访问权限,如节点数据读写、节点创建、节点删除、读取子节点列表、设置节点权限等。

在传统的文件系统中,ACL分为两个维度:组和权限。一个组可以包含多种权限,一个文件或目录拥有了某个组的权限即拥有了组里的所有权限。文件或子目录默认会继承其父目录的ACL。

而在zookeeper中,znode的ACL是没有继承关系的,每个znode的权限都是独立控制的,只有客户端满足znode设置的权限要求时,才能完成相应的操作。Zookeeper的ACL分为三个维度:授权策略scheme、用户id、用户权限permission。

1.3 Paxos算法

1.3.1 算法简介

Paxos算法是莱斯利·兰伯特1990年提出的一种基于消息传递的、具有高容错性的一致性算法。Google chubby(分布式锁服务)的作者说过,世上只有一种一致性算法,那就是Paxos

,所有其他一致性算法都是Paxos的不完整版。Paxos算法是一种公认的晦涩难懂的算法,并且工程实现上也具有很大的难度。较有名的Paxos工程实现有google chubby,ZAB,微信的PhxPaxos等。

Paxos算法是用于解决什么问题的呢?Paxos算法要解决的问题是,在分布式系统中如何就某个决议达成一致。

1.3.2 Paxos与拜占庭将军问题

拜占庭将军问题,是由Paxos算法作者提出的点对点通信中的基本问题。该问题要说明的含义是,在存在消息丢失的不可靠信道上试图通过消息传递的方式达到一致性是不可能的。

Paxos算法的前提是不存在拜占庭将军问题,即信道是安全的、可靠的,集群节点间传递消息是不会被篡改的。在实际工程实践中,大多数系统都是部署在一个局域网内,因此消息被篡改的情况很少;另一方面,由于硬件和网络原因而造成的消息不完整问题,现在已经不再是问题,只需要一套简单的校验算法即可。因此,在实际工程中各个服务器间消息传递过程可以认为不存在拜占庭将军问题。

一般情况下,分布式系统中各个节点间采用两种通讯模型:共享内容(shared memory)、消息传递(messages passing)。而Paxos是基于消息传递通讯模型的。

1.3.3 算法描述

(1)三种角色

在Paxos算法中有三种角色,分别具有三种不同的行为。但很多时候,一个进程可能同时充当多个角色。

  1. Proposer:提案(Proposal)的提议者。
  2. Acceptor:提案的表决者,即是否accept该提案。只有半数(一般集群数量是奇数)以上的Acceptor接受了某提案,那么该提案者被认定为选定。
  3. Learners:提案的学习者。当提案被选定后,其会同步并执行提案。

一个提案的表决者(Acceptor)会存在多个,但在一个集群中,提议者(Proposer)也是可能存在多个的,不同的提议者(Proposer)会提出不同的提案。而一致性算法则可以保证如下几点:

  1. 没有提案被提出则不会有提案被选定。
  2. 每个提议者在提出提案时都会为该提案指定一个具有全局唯一性的、递增的提案编号N,即在整个集群中是唯一的。
  3. 每个表决者在accept某提案后,将会把该提案的编号N记录在本地,这样每个表决者中保存的已经被accept的提案中会存在一个编号最大的提案,其编号假设为maxN。每个表决者仅会accept变好大于自己本地maxN的提案。
  4. 在众多提案中最终只能有一个提案被选定。
  5. 一旦一个提案被选定,则其他服务器会主动同步(Learn)该提案到本地。

(2)算法过程描述

Paxos算法的执行过程划分为两个阶段:准备阶段prepare与接受阶段accept。

prepare阶段

  1. 提议者(Proposer)准备提交一个编号为N的提议,于是其首先向所有表决者(Acceptor)发送prepare(N)请求,用于试探集群是否支持该编号的提议。
  2. 每个表决者(Acceptor)中都保存着自己曾经accept过的提议中的最大编号maxN。当一个表决者接收到其他主机发送来的prepare(N)请求时,其会比较N和MaxN的值。若N小于maxN,则说明该提议已经过时,当前表决者采取不回应或者回应Error的方式来拒绝该prepare(N)请求;若N大于等于maxN,则说明该提议是可以接受的,当前表决者会将其曾经已经accept的编号最大的提案Proposal(myid,maxN,value)反馈给提议者,以向提议者展示自己支持的提案意愿。其中第一个参数myid表示决议者Acceptor的标识id,第二个参数表示其曾接受的提案的最大编号maxN,第三个参数表示该提案的真正内容value。当然,若当前表决者还未曾accept过任何提议,则会将Proposal(myid,null,null)反馈给提议者。

accept阶段

  1. 当提议者(Proposer)发出prepare(N)后,若收到了超过半数的表决者(Accepter)的反馈,那么该提议者就会将真正的提案Proposal(N,value)发送给所有的表决者。
  2. 当表决者(Acceptor)接收到提议者发送的Proposal(N,value)提案后,会再次拿出自己曾经accept过的提议中最大编号maxN,及曾经反馈过的prepare(N)的最大编号,让N与它们进行比较,若N大于等于这两个编号,则当前表决者accept该提案,并反馈给提议者。若N小于这两个编号,则表决者采取不回应或回应Error的方式来拒绝该提议。
  3. 若提议者没有接收到超过半数的表决者的accept反馈,则重新进入prepare阶段,递增提案号,重新提出prepare请求。若提议者接收到的反馈数量超过了半数,则其他的未向提议者发送accept反馈的表决者将成为Leaner,主动同步提议者的提案。

(3)算法过程举例

zookeeper、Paxos算法、ZAB协议、CAP理论介绍_第3张图片

假设有三台主机,他们要从中选出一个Leader。这三台主机在不同的时间分别充当提案的提议者Proposer、表决者Acceptor及学习者Learnor三种不同的角色。

这里首先介绍一下该举例的前提:每个提议者(Proposer)都想提议自己要当Leader,假设三个提议者Proposer-1、Proposer-2、Proposer-3提议的Proposer-1初始编号依次为20、10、30.每个提议者都要将提案发送给所有的表决者(Acceptor),为了便于理解,假设都只有两个(超过半数)表决者收到消息:Accepter-2与Acceptor-3收到Proposer-2的消息;Accepter-1与Acceptor-2收到了Proposer-1的消息;Accepter-2与Acceptor-3收到了Proposer-3的消息。

A、prepare阶段

假设Proposer-1发送的prepare(20)消息先达到Acceptor-1和Acceptor-2。因为他们之前都没有接受过prepare请求,所以他们都是直接接收该请求,并将Proposal(server1-id,null,null)与Proposal(server2-id,null,null)反馈给提议者Proposer-1,同时Acceptor-1和Acceptor-2记录下目前收到的最大提议编号maxN为20,即其以后不会再接受编号小于20的请求。Proposer-1收到了超过半数的反馈。

紧接着Proposesr-2的prepare(10)消息到达Acceptor-2和Acceptor-3.由于Acceptor-3还未曾接受过其他请求,所以其直接接受Proposer-2的prepare(10)的请求,并返回Proposer-2 Proposal(server3-id,null,null),Acceptor-3会记录下其目前收到的最大提议编号maxN为10,即其以后不会再接受编号小于10的请求。另外对于Acceptor-2来说,由于其已经接受Proposer-1的提案20,则会回应error或者不回应的方式告诉Proposer-2不接受其提案,此时Proposeser-2接收到的反馈数量是1,没有超过半数,则会递增自己的编号,这里我们默认递增10,然后会继续重复发送prepare()

B、accept阶段

a、Proposer的提交

  1. Proposer-1收到过半反馈,且这些反馈均未标识自己支持的提案意愿,即反馈的value为null,所以Proposer-1就直接向Acceptor-1和Acceptor-2提交了Proposal(server1-id,20,server1)的提案。
  2. Proposer-2收到了过半反馈,且这些反馈均未表示自己支持的提案意愿,即反馈的value为null,所以Proposer-2就直接向Acceptor-2和Acceptor-3提交了Proposal(server2-id,40,server2)的提案,即递增自己的编号。
  3. Proposer-3收到过半反馈,且这些反馈均未标识自己支持的提案意愿,即反馈的value为null,所以Proposer-3就直接向Acceptor-2和Acceptor-3提交了Proposal(server3-id,30,server3)的提案。

b、Acceptor的表决

  1. Acceptor-1和Acceptor-2接收到Proposer-1的提案Proposal(server1-id,20,server1)请求,由于Acceptor-1不能接收编号小于20的提案,但可以接收等于20的提案,所以其通过了该提案,但由于Acceptor-2不能接收编号小于40的提案,所以其拒绝了该提案。
  2. Acceptor-2和Acceptor-3接收到Proposer-2的提案Proposal(server2-id,40,server2),它们均通过了该提案。
  3. Acceptor-2和Acceptor-3接收到Proposer-3的提案Proposal(server3-id,30,server3),它们均拒绝了该提案。

由于Acceptor-2和Acceptor-3已经通过了提案Proposal(server2-id,40,server2),并达成了超过半数的一致性,server2马上成为了Leader,选举状态结束。Server2会发布广播给所有Leaner,通知它们来同步数据。同步完成后,集群进入正常服务状态。

1.3.4 Paxos算法优化

前面所述的Paxos算法在实际工程应用过程中,根据不同的实际需求存在诸多不便之处,所以也就出现了很多对于基本Paxos算法的优化算法,例如,Multi Paxos、Fast Paxos、EPaxos。而Zookeeper的Leader选举算法FastLeaderElection则是Fast Paxos算法的工程应用。

1.4 ZAB协议

1.4.1 ZAB协议简介

ZAB,zookeeper Atomic Broadcast,zk原子消息广播协议,是专为zookeeper设计的一种支持崩溃回复的原子广播协议,是一种Paxos协议的优化算法,在Zookeeper中,主要依赖ZAB协议来实现分布式数据一致性。

Zookeeper使用一个但一进程来接受并处理客户端的所有事务请求,即写请求。当服务器数据的状态发生变更后,集群采用ZAB原子广播协议,以事务提案Proposal的形式广播到所有的副本进程上。ZAB协议能够保证一个全局的变更序列,即可以为每一个事务分配一个全局的递增编号xid。

当Zookeeper客户端连接到Zookeeper集群的一个节点后,若客户端提交的是读请求,那么当前节点就直接根据自己保存的数据对其进行相应;如果是写请求且当前节点不是Leader,那么节点就会将该写请求转发给Leader,Leader会以提案的方式广播该写操作,只要有超过半数节点统一该写操作,则该写操作请求就会被提交。然后Leader会再次广播给所有订阅者,即Leaner,通知它们同步数据。

1.4.2 三类角色

为了避免Zookeeper的单点问题,zk也是以集群的形式出现的。Zk集群中的角色主要有一下三类:

  1. Leader:zk集群写请求的唯一处理者,并负责进行投票的发起和决议,更新系统状态。Leader是很民主的,并不是说其在接收到写请求后马上就修改其中保存的数据,而是首先根据写请求提出一个提议,在大多数zkServer均同意时才会做出修改。
  2. Follower:接收客户端请求,处理读请求,并向客户端返回结果;将写请求转给Leader;在选主(Leader)过程中参与投票。
  3. Observer:可以理解为无选举投票权与写操作投票权的Follower,其不属于法定人数范围,主要是为了协助Follower处理更多的读请求。如果Zookeeper集群的读请求负载很高时,势必要增加处理读请求的服务器数量。若增加的这写服务器都是以Follwer的身份出现,则会大大降低写操作的效率。因为Leader发出的所有写操作,均需要通过法定人数半数以上同意。过多的Follower会延长Leader与Follower的通信压力,降低写操作效率。同样,过多的Follower会延长Leader的选举时长,降低整个集群的可用性。此时,可选择增加Observer服务器,既提高了处理器读操作的吞吐量,又没有增加法定人数。只要法定人数不变,无论是写操作投票还是选举投票,其都不会增加通信压力,都不会影响投票效率。

1.4.3 三种模式.

ZAB协议中对zkServer的状态描述有三种模式:恢复模式、同步模式和广播模式。

  1. 恢复模式:在服务重启过程中,或在Leader崩溃后,就进入了恢复模式,要恢复到zk集群正常的工作状态。
  2. 同步模式:在所有的zkServer启动完毕,或Leader崩溃后又被又被选举出来时,就进入了同步模式,各个Follower需要马上将Leader中的数据同步到自己主机中。当大多数zkServer完成了与Leader的状态同步以后,恢复模式就结束了。所以,同步模式包含在恢复模式过程中。
  3. 广播模式:当Leader的提议被大多数zkServer同意后,Leader会修改自身数据,然后会将修改后的数据广播给其他Follower。

1.4.4 zxid

Zxid为64位长度的Long类型,其中高32位标识纪元epoch,低32位标识事务标识xid,即zxid由两部分构成:epoch与xid。

每个Leader都会具有不同的epoch值,表示一个时期、时代。每一次新的选举开启后都会生成一个新的epoch,新的Leader产生,则会更新所有zkServer的zxid中的epoch。

Xid则为zk的事务id,每一个写操作都是一个事务,都会有一个xid。xid为一个依次递增的流水号。每一个写操作都需要由Leader发起一个提案,由所有Follower表决是否同意本次写操作,而每个提案都具有一个zxid。

1.4.5 消息广播算法

当集群中已经有过半的Folower与Leader服务器完成了状态同步,那么整个zk集群就可以进入消息广播模式了。

如果集群中的其他节点收到客户端的事务请求,那么这些非Leader服务器会首先将这个事务请求转发给Leader服务器。Leader服务器会为其生成对应的事务提案Proposal,并将其发送给集群中其余其余所有主机,然后再分别收集他们的选票,在选票过半后进行事务提交。其具体过程如下:

zookeeper、Paxos算法、ZAB协议、CAP理论介绍_第4张图片

  1. Leader接收到消息请求后,将消息赋予一个全局唯一的64位自增id,即zxid,通过zxid的大小比较即可实现事务的有序性管理。
  2. 为了保证Leader向Follower发送提案的有序,Leader会为每个Follower创建一个FIFO队列,并将提案副本写入到各个队列。然后再通过这些队列将提案发送给各个Follower。
  3. 当Follower接收到提案后,会先将提案的zxid与本地记录的事务日志中最大的zxid进行比较。若当前提交的zxid大于最大zxid,则将当前提案记录到本地事务日志中,并向Leader返回一个ACK。
  4. 当Leader接收到过半的ACKs后,对于之前回复过Leader的Follower,Leader会向其发送COMMIT消息,批准这些Follower在本地执行该消息;对于之前未回复过Leader的Follower,Leader会将这些Follower对应的队列中的提案发送给这些Follower,发送的同时会携带COMMIT消息。当Follower收到COMMIT消息后,就会执行该消息。

1.4.6 恢复模式的两个原则

当集群正在启动过程中,或Leader与超过半数的主机断连后,集群就进入了恢复模式。对于要恢复的数据状态需要遵循两个原则。

(1)已被处理过的消息不能丢

当Leader收到超过半数Follower的ACKs后,就向各个Follower广播COMMIT消息,批准各个Server执行该写操作事务。当各个Server在接收到Leader的COMMIT消息后会在本地执行该写操作,然后会向客户端响应写操作成功。但是如果在非全部Follower收到COMMIT消息之前Leader就挂了,这将导致一种后果:部分Server已经执行了该事务,而部分Server尚未收到COMMIT消息,所以其并没有执行该事务。当新的Leader被选举出,集群经过恢复模式后需要保证所有Server上都执行了那些已经被部分Server执行过的事务。

为了保证已被处理过的消息不能丢的目的,ZAB的恢复模式使用了以下策略:

  1. 选举拥有proposal最大值(即zxid最大)的节点作为新的Leader:由于所有提案被COMMIT之前必须有合法数量的Follower ACK,即必须有合法数量的服务器日志上有该提案,因此,只要有合法数量的节点正常工作,就必然有一个节点保存了所有被COMMIT消息的proposal状态。
  2. 新的Leader先将自身拥有而并非所有Folower都有的proposal发送给Follower,再将这些proposal的COMMIT命令发送给Follower,以保证所有的Follower都保存并执行了所有的proposal。通过以上策略,能保证已经被处理的消息不会丢。

(2)被丢弃的消息不能再现

当Leader接收到事务请求并生成了proposal,但还未向Follower发送时就挂了。由于其他Follower并没有收到此proposal,即并不知道该Proposal的存在,因此在经过恢复模式重新选举产生了新的Leader后,这个事务被跳过。在整个集群尚未进入正常服务状态时,之前挂了的Leader主机重新启动并注册成为了Follower。但由于保留了被跳过的proposal,所以其与整个系统的状态是不一致的,需要将该proposal删除。

ZAB通过设计巧妙的zxid实现了这一目的。一个zxid是64位,高32位是纪元epoch编号,每一次选举epoch的值都会增一。低32位是事务标识xid,每产生一个事务,该xid值都会增一。这样设计的好处是旧的Leader挂了后重启,它不会被选举为新的Leader,因此此时他的zxid肯定小于当前新的epoch。当旧的Leader作为Follower接入新的Leader后,新的Leader会让其将所有旧的epoch编号号的、未被COMMIT的proposal清除。

1.4.7 Leader选举算法

当集群正在启动过程中,或Leader与超过半数的主机断连后,集群就进入了恢复模式。而恢复模式中最重要的阶段就是Leader选举。

在集群启动过程中的Leader选举过程(算法)与Leader断连后的Leader选举过程稍微有一些区别,基本相同。

(1)集群启动中的Leader选举

若进行Leader选举,则至少需要两台主机,这里以三台主机组成的集群为例。

在集群初始化阶段,当第一台服务器Server1启动时,会给自己投票,然后发布自己的投票结果。投票包含所推举的服务器的mid和ZXID,使用(mid,ZXID)来表示,此时Server1的投票为(1,0)。由于其他机器还没有启动所以他们收不到反馈信息,Server1的状态一直属于Looking,即属于非服务状态。

当第二台服务器Server2启动时,此时两台机器可以相互通信,每台机器都试图找到Leader,选举过程如下:

  1. 每个Server发出一个投票。此时Server1的投票为(1,0),Server2的投票为(2,0),然后各自将这个投票发给集群中的其他机器。
  2. 接受来自各个服务器的投票。集群的每个服务器收到选票后,首先判断该选票的有效性,如检查是否是本轮投票、是否来自LOOKING状态的服务器。
  3. 处理投票。针对每一个投票,服务器都需要将别人的投票和自己的投票进行PK,PK规则如下:
  1. 优先检查ZXID。ZXID比较大的服务器优先作为Leader。
  2. 如果ZXID相同,那么就比较myid。Myid较大的服务器作为Leader服务器。

对于Server1而言,它的投票是(1,0),接受Server2的投票为(2.,0)。其首先会比较两者的ZXID,均为0,在比较myid,此时Server2的myid最大,于是Server1更新自己的投票为(2,0),然后重新投票。对于Server2而言,其无须更新自己的投票,只是再次向集群中所有主机发出上一次投票信息即可。

  1. 投票统计。每次投票后,每一台zkServer都会统计投票信息,判断是否已经有过半机器接收到相同的投票信息。对于Server1,Server2而言,都统计出集群中已经有两台主机接受了(2,0)的投票信息,此时便认为已经选出了新的Leader,即Server2.
  2. 改变服务器状态。一旦确定了Leader,每个服务器就会更新自己的状态,如果是Follower,那么就变更为FOLLOWING,如果是Leader,就变更为LEADING。
  3. 添加主机。在新的Leader选举出来后Server3启动,其想发出一轮新的选举。但由于当前集群中各个主机的状态并不是LOOKING,而是各司其职的正常服务,所以其只能是以Follower的身份加入到集群中。

(2)断连后的Leader选举

在Zookeeper运行期间,Leader与非Leader服务器各司其职,即便当有非Leader服务器宕机或新加入时也不会影响Leader。但是若Leader服务器挂了,那么整个集群将暂停对外服务,进入新一轮的Leader选举,其过程和启动时期的Leader选举过程基本一致。

假设正在运行的有Server1,Server2,Server3三台服务器,当前Leader是Server2,若某一时刻Server2挂了,此时便开始新一轮的Leader选举了。选举过程如下:

  1. 变更状态。Leader挂后,余下的非Observer服务器都会将自己的服务器状态由FOLLOWING变更为LOOKING,然后开始进入Leader选举过程。
  2. 每个Server会发出一个投票,仍然会首先投自己。不过,在运行期间每个服务器上的ZXID可能是不同,此时假定Server1的ZXID为111,Server3的ZXID为333;在第一轮投票中,Server1和Server3都会都会投自己,产生投票(1,111),(3,333),然后各自将投票发送给集群中所有服务器。
  3. 接受来自各个服务器的投票。与启动时过程相同。集群的每个服务器收到投票后,首先判断该投票的有效性,如检查是否是本轮投票、是否来自LOOING状态的服务器。
  4. 处理投票。与启动过程相同。针对每一个投票,服务器都需要将别人的投票和自己的投票进行PK。对于Server1而言,他的投票是(1,111),接收Server3的投票为(3,333)。其首先会比较两者的ZXID,Server3投票的zxid为333大于Server1投票的zxid的111,于是Server1更新自己的投票为(3,333),然后重新投票。对于Server3而言,其无须更新自己的投票,只是再次向集群中所有主机发出上一次投票信息即可。
  5. 统计投票。与启动时过程相同。经过票数统计,最终Server3当选新的Leader。
  6. 改变服务器的状态。与启动时过程相同。一旦确定了Leader,每个服务器就会更新自己的状态。Server1变更为FOLLOWER,Server3变更为LEADING。

1.4.8 恢复模式下的数据同步

当完成Leader选举后,就要进入到恢复模式下的数据同步阶段。Leader服务器会为每一个Follower服务器准备一个队列,并将那些没有被各个Follower服务器同步的事务以Proposal的形式逐条发给各个Follower服务器,并在每一个Proposal后紧跟一个commit消息,表示该事务已经被提交,Follower可以直接接收并执行。当Follower服务器将所有尚未同步的事务proposal都从leader服务器同步过来并成功执行后,会向准leader发送ACK信息。Leader服务器在收到该ACK后就会将该follower加入到真正可用的follower列表。

1.5 CAP原则

1.5.1 简介

CAP原则又称CAP定理,指的是在一个分布式系统中,Consistency(一致性)、Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。

  1. 一致性(C):分布式系统中的所有主机中在同一时刻是否可以保证具有完全相同的数据备份。若具有,则该分布式系统具有一致性。
  2. 可用性(A):在集群中部分节点发生故障后,是否会影响对客户端读写请求的响应。注意,若在短时间内会影响,其也不具备这里所说的可用性。
  3. 分区容错性(P):分布式系统中的一台主机称为一个分区。那么,什么是分布式系统的错误呢?分布式系统中各个主机无法保证数据的一致性是一种错误;分布式系统无法及时响应客户端请求,即不具有可用性也是一种错误。对于分布式系统,必须要具有对这些错误的包容性,即容错性,这是必须的。

1.5.2 三二原则

对于分布式系统,在CAP原则中分区容错性P是必须要保证的。但其并不能同时保证一致性与可用性。因为现在的分布式系统在满足一致性的前提下,会牺牲可用性;在满足了可用性的前提下,会牺牲一致性。所以,CAP原则对于一个分布式系统来说,只可能满足两项,即要么CP,要么AP。这就是CAP的三二原则。

1.5.3 ZK与CP

Zk遵循的是CP原则,即保证了一致性,但牺牲了可用性。体现在哪里呢?

当Leader宕机后,zk集群会马上进行新的Leader的选举。但选举时长在30-120秒之间,整个选举期间选举期间kzk集群是不接受客户端的读写操作的,即zi集群是出于瘫痪状态的。所以,其不满足可用性。

为什么Leader的选举需要这么长时间呢?为了保证zk集群各个节点中数据的一致性,zk集群做了两类数据同步:初始化同步与更新同步。当新的Leader被选举出后,各个Follower需要将新的Leader的数据同步到自己的缓存中,这是初始化同步;当Leader的数据被客户端修改后,其会向Follower发出广播,然后各个Follower会主动同步Leader的更新数据,这是更新同步。无论是初始化同步还是更新同步,zk集群为了保证数据的一致性,若发现超过半数的Follower同步超时,则其会再次进行同步,而这个过程中zk集群是处于不可用状态的。

由于zk采用了CP原则,所以导致其可用性降低,这是致命的问题。Spring Cloud的Eureka在分布式系统中所起的作用类似于zk,但其采用了AP原则,其牺牲了一致性,但保证了可用性。

你可能感兴趣的:(zookeeper)