ZooKeeper简介
ZooKeeper是一个开源的分布式应用程序协调服务器,其为分布式系统提供一致性服务。其一致性是通过基于Paxos算法的ZAB协议完成的。其主要功能包括:配置维护、域名服务、分布式同步、集群管理等。
功能简介
配置维护
分布式系统中,很多服务都是部署在集群中的,即多台服务器中部署着完全相同的应用,起着完全相同的作用。当然,集群中的这些服务器的配置文件是完全相同的。
若集群中服务器的配置文件需要进行修改,那么就需要逐台修改这些服务器中的配置文件。如果集群中服务器较少,那么修改还不是太麻烦,但如果服务器特别多,比如某些大型互联网公司的Hadoop集群有数千台服务器,那么纯手工的更改这些配置文件几乎就是一件不可能完成的任务,即使使用大量人力进行修改可行,但过多的人员参与,出错的概率大大提升,对于集群形成的危险是很大的。
这时候ZooKeeper就可以派上用场了。其对于配置文件的维护采用的是“发布/订阅模型”。发布者将修改好的集群配置文件发布到ZooKeeper服务器的文件系统中,那么订阅者马上就可以接收到通知,并主动去同步ZooKeeper里的配置文件。ZooKeeper具有同步操作的原子性,确保每个集群服务器的配置文件都能正确更新。
域名服务
在分布式应用中,一个项目包含多个工程,而这些工程中,有些工程专门为其他工程提供服务的。一个项目中可能会存在多种提供不同服务的工程,而一种服务可能存在多个提供者(服务器)。所以,用于消费这些服务的客户端工程若要消费这些服务,就变得异常复杂了。
此时,ZooKeeper就可以上场了。为每个服务器起个名称,将这些服务器的名称与提供这些服务的主机地址注册到ZooKeeper中,形成一个服务器映射表,服务消费者只需通过服务名称即可享受服务,而无需了解服务具体的提供者是谁,服务的减小、添加、变更,只需修改ZooKeeper中的服务器映射表即可。
阿里的Dubbo就是使用Zookeeper作为域名服务器的。
分布式同步
在分布式系统中,很多运算(对请求的处理)过程是由分布式集群中的若干服务服务器共同计算完成的,并且它们之间的运算还具有逻辑上的先后顺序。
使用ZooKeeper可以协调这些服务器运算的过程。让这些服务器都同时监听ZooKeeper上的同一个znode(ZooKeeper文件系统中的一个数据存储节点),一旦其中一个服务器Update znode,那么另一个相应服务器就能够收到通知,并作出相应处理。
集群管理
集群管理中最麻烦的就是节点故障管理。ZooKeeper就可以让集群选出一个健康的节点作为Master,Master随时监控着当前集群中的每个节点的健康状况,一旦某个节点发生故障,Master会把这个情况通知给集群中的其他节点,使其它节点对于任务的分配做出相应调整。ZooKeeper不仅可以发现故障,也会对故障做出甄别,如果该故障可以修复,ZooKeeper可以自动修复,若不能修复则会告诉系统系统管理员迅速定位问题。
但是这里还有一个问题:Master故障了,那怎么办?ZooKeeper内部有一个“选举算法”,当Master故障出现时,ZooKeeper能马上选出新的Master对集群进行管理。
一致性要求
什么是zk一致性?其需要满足以下几点要求:
顺序一致性
从同一个客户端发起的n多个事务请求,最终将会严格按照其发起顺序被应用到ZooKeeper中。
原子性
所有事物请求的结果在集群中所有机器上的应用情况是一致的,也就是说要么整个集群所有主机都成功应用了某一个事务,要么都没应用,不会出现集群中部分主机应用了该事务,而另外一部分没有应用的情况。
单一视图
无论客户端连接的是哪个ZooKeeper服务器,其看到的服务端数据模型都是一致的。
可靠性
一旦服务端成功地应用了一个事务,并完成对客户端的响应,那么该事务所引起的服务端状态变更将会被一直保留下来,除非有另一个事务又对其进行变更。
实时性
通常人们看到实时性的第一反应是,一旦一个事务被成功应用,那么客户端能够立即从服务端上读取到这个事务变更后的最新数据状态。这里需要注意的是,ZooKeeper仅仅保证在一定的时间段内,客户端最终一定能够从服务端上读取到最新的数据状态。
ZooKeeper中的重要概念
Session
Session是指客户端会话。ZooKeeper对外的服务端口默认是2181,客户端启动时,首先会与zk服务器建立一个TCP长连接,从第一个连接建立开始,客户端会话的生命周期也开始了。通过这个长连接,客户端能够通过心跳检测保持与服务器的有效会话,也能够向ZooKeeper服务器发送请求并接受响应,同时还能通过该连接接收来自服务器的Watcher事件通知。
Session的SessionTimeout值用来设置一个客户端会话的超时时间。当由于服务器压力太大、网络故障或是客户端主动断开连接等各种原因导致客户端连接断开时,只要在SessionTimeout规定的时间内客户端能够重新连接上集群中任意一台服务器,那么之前创建的会话仍然有效。
znode
ZooKeeper的文件系统采用树形层次化的目录结构,与Unix文件系统非常相似。每个目录在ZooKeeper中叫做一个znode,每一个znode拥有一个唯一的路径标识,即名称。znode可以包含数据和子znode(临时节点不能有子znode)。znode中的数据可以有多个版本,所以查询某路径下的数据需带上版本号。客户端应用可以在znode上设置监视器(Watcher)。
Watcher机制
zk通过Watcher机制实现了发布/订阅模式。zk提供了分布式数据的发布/订阅功能,一个发布者能够让多个订阅者同时监听某一主题对象,当这个主题对象发生变化时,会通知所有订阅者,使它们能够做出相应处理。zk引入了Watcher机制实现这种分布式通知功能。zk允许客户端(订阅者)向服务端(发布者)注册一个Watcher监听。当服务端的一些指定事件触发这个Watcher,那么就会向指定客户端发送一个事件通知。而这个事件通知则是通过TCP长连接的Session完成的。
ACL
ACL全称为Access Control List(访问控制列表),用于控制资源的访问权限,是zk数据安全的保障。zk利用ACL策略控制znode节点的访问权限,如节点数据读写、节点创建、节点删除、读取子节点列表、设置节点权限等。
在传统的文件系统中,ACL分为两个维度:组与权限。一个属组可以包含多种权限,一个文件或目录拥有某个组的权限即拥有了组里的所有权限。文件或子目录默认会继承其目录的ACL。
而在ZooKeeper中,znode的ACL是没有继承关系的,每个Znode的权限都是独立控制的,只有客户端满足znode设置的权限要求时,才能完成相应的操作。ZooKeeper的ACL分为三个维度:授权策略scheme、用户id、用户权限permission。
Paxos算法
算法简介
Paxos算法是莱斯利.兰伯特(Leslie Lamport)1990年提出的一种关于消息传递的、具有高容错性的一致性算法。Google Chubby(分布式锁服务)的作者Mike Burrows说过,世上只有一种一致性算法,那就是Paxos,所有其它一致性算法都是Pxos算法的不完整版。Paxos算法是一种公认的晦涩难懂的算法,并且实现上也具有很大难度。较有名的Paxos工程实现有Google Chubby、ZAB、微信的PhxPaxos等。
Paxos算法要解决的问题是在z布o系统中如何就某个协议达成一致。
Paxos与拜占庭将军问题
拜占庭将军问题(Byzantine failures),是由Paxos算法作者莱斯利.兰伯特提出的点对点通信中基本问题。该问题要说明的含义是,在存在消息丢失的不可靠信道上试图通过信息传递的方式达到一致性是不可能的。
Paxos算法前提是不存在拜占庭将军问题,即信道是安全的、可靠的,集群节点间传递的消息是不会被篡改的。在实际工程实践中,大都数系统都是部署在一个局域网内,因此消息被篡改的情况很小;另一方面,由于硬件和网络原因而造成的信息不完整问题,现在已经不再是问题,只需要一套简单的校验算法即可,因此,在实际工程中各个服务器间消息传递过程中可以认为不存在拜占庭将军问题。
一般情况下,分布式系统中各个节点之间采用两种通讯模型:共享内存(Shared Memory)、消息传递(Message Passing)。而Paxos是基于消息传递通讯模型的。
算法描述
三种角色
在Paxos算法中有三种角色,分别具有三种不同的行为。但很多时候,一个进程可能同时充当着多种角色。
Proposer:提案(Proposal)的提议者。
Acceptor:提案的表决者,即是否accept该提案。只有半数以上的Acceptor接受某提案,那么该提案则被认定为“选定”。
Learners:提案的学习者。当提案被选定,其会同步并执行提案。
一个提案的表决者(Acceptor)会存在多个,但在一个集群中,提议者(Proposer)是可能存在多个的,不同的提议者(Proposer)会提出不同的提案。一致性算法可以保证如下几点:
没有提案被提出则不会有提案被选定。
每个提议者在提出提案时都会为该提案指定一个具有全局唯一性的、递增的提案编号N,即在整个集群中是唯一的。
每个表决者在accept某提案后,会将该提案的编号记录在本地,这样每个表决者中保存的已经被accept的提案中会存在一个编号很大的提案,其编号假设为maxN,每个表者仅会accept编号大于自己本地maxN的提案。
在众多提案中最终只能只有一个提案被选定。
一旦一个提案被选定,则其它服务器会主动同步(Learn)该提案到本地
算法过程描述
Paxos算法执行过程划分为两个阶段:准备阶段prepare与接受阶段。
prepare阶段
提议者(Proposer)准备提交一个编号为N的提议,于是其向所有表决者(Acceptor)发送prepare(N)请求,用于试探集群是否支持该编号的提议。
每个表决者(Acceptor)中都保存着自己曾经accept过的提议中的最大编号maxN。当一个表决者接收到其他主机发送的prepare(N)请求时,其会比较N与maxN的值。若B小于maxN,则说明该提议已过时,则当前表决者采取不回应或回应Error的方式来拒绝该prepare(N)请求;若N大于等于maxN,则说明该提议是可以接受的,当前表决者会将其曾经已经accept的编号最大的提案Proposal(myid, maxN, value)反馈给提议者,以向提议者展示自己支持的提案意见。其中第一个参数myid表示表决者Acceptor的标识id,第二个参数表示其曾接受的提案的最大编号maxN,第三个参数表示该提案的真正内容value。当然,若当前表决者还未曾accept过任何协议,则会将Proposal(myid,null,null)反馈给提议者。
accept阶段
当提议者(Proposal)发出prepare(N)后,若收到了超过半数的表决者(acceptor)的反馈,那么该提议者就会将其真正的提案Proposal(N,value)发送给所有的表决者。
当表决者(Acceptor)接收到提议者发送的Proposal(N,Value)提案后,会再次拿出自己曾经accept过的提议中最大编号maxN,及曾经反馈过的prepare的最大编号,让N与它们进行比较,若N大于等于这两个编号,则当前表决者accept该提案,并反馈给提议者。若N小于这两个编号,则表决者采取不回应或回应Error的方式来拒绝该提议。
若提议者没有接收到超过半数的表决者的accept反馈,则重新进入prepare阶段,递增提案号,重新提出prepare请求。若提议者接收到的反馈数量超过了半数,则其它的未向提议者发送accept反馈的表决者将成为Learner,主动同步提议者该提案。
Paxos算法优化
前面所述的Paxos算法在实际工程应用过程中,根据不同实际需求存在许多不便之处,所以也就出现了很多对于基本Paxos算法的优化算法,例如,Multi Paxos、Fast Paxos、EPaxos。ZooKeeper的Leader选举算法FastLeaderElection则是Fast Paxos算法的工程应用。
ZAB协议简介
ZAB,ZooKeeper Atomic Broadcast,zk原子消息广播协议,是专为ZooKeeper设计的一种支持崩溃恢复的原子广播协议,是一种Paxos协议的优化算法。在ZooKeeper中,主要依赖ZAB协议来实现分布式数据一致性。
ZooKeeper使用一个单一主进程来接收并处理客户端的所有事务请求,即写请求。当服务数据的状态发生变更后,集群采用ZAB原子广播协议,以事务提案Proposal的形式广播到所有的副本进程上。ZAB协议能够保证一个全局的变更序列,即可以为每一个事务分配一个全局递增编号xid。
当ZooKeeper客户端连接到ZooKeeper集群的一个节点之后,若客户端提交的是读请求,那么当前节点直接根据自己保存的数据对其进行响应;如果是写请求且当前节点不是Leader,那么节点就会将该写请求转发给Leader,Leader会以提案的方式广播该写操作,只有超过半数的节点同意该写操作,则该写操作就会被提交。然后Leader会再次广播所有订阅者,即Leader,通知它们同步数据。
三类角色
为了避免ZooKeeper的单点问题,zk是以集群的方式出现的。zk集群中的角色主要有以下三类:
Leader:zk集群写请求的唯一处理者,并负责进行投票的发起和决议,更新系统状态。Leader是很民主的,并不是说其在接收到写请求后马上就修改其中保存的数据,而是首先根据写请求提出一个提议,在大多数zkServer均同意时才做出修改。
Follower:接收客户端请求,处理读请求,并向客户端返回结果;将写请求转给Leader;在选主(选Leader)过程中参与投票。
Observer:可以理解为无选主投票权与写操作投票权的Follower,其不属于法定人范围,主要是为了协助Follower处理更多的读请求。如果ZooKeeper集群的读请求负载很高时,势必要增加处理读请求服务器数量。若增加这些服务器都是以Follower的身份出现,则会大大降低写操作的效率。因为Leader发出的所有写操作提议,均需要通过法定人数半数以上同意。过多的的Follower会增加Leader与Follower的通信压力,降低写操作效率。同样,过多的Follower会延长Leader的选举时长,降低整个集群的可用性。此时,可选择增加Observer服务器,既提高了处理读操作的吞吐量,又没有增加法定人数。只要法定人数不变,无论是写操作还是选主操作投票,其都不会增加通信压力,都不会影响投票效率。
三种模式
ZAB协议中zkServer的状态描述有三种模式:恢复模式、同步模式和广播模式。
恢复模式:在服务重启过程中,或在Leader蹦溃后,就进入了恢复模式,要恢复到zk集群正常的工作状态。
同步模式:在所有的zkServer启动完毕,或Leader崩溃后又被选举出来时,就进入了同步模式,各个Follower需要马上将Leader中的数据同步到自己的主机中。当大多数zkServer完成了与Leader中的数据同步到自己主机中,当大多数zkServer完成了与Leader的状态同步以后,恢复模式就结束了。所以,同步模式包含在恢复模式过程中。
广播模式:当Leader的提议被大多数zkServer同意后,Leader会修改自身数据,然后会将修改后的数据广播给其它Follower。
zxid
zxid为64为64位长度的Long类型,其中高32位表示纪元epoch,低32位表示事务标识xid。即zxid由两部分构成:epoch与xid。
每个Leader都会具有不同的epoch值,表示一个时期、时代。每一次新的选举开启时都会生成一个新的epoch,新的Leader产生,则会更新所有zkServer,则会更新所有zkServer的zxid中的epoch。
xid则为zk的事务id,每一个写操作都是一个事务,都会有一个xid。xid为一个依次递增的流水号。每一个写操作都需要由Leader发起一个提案,由所有Follower表决是否同意本次写操作,而每个提案都具有一个zxid。
消息广播算法
当集群中已经有过半的Follower与Leader服务器完成了状态同步,那么整个zk集群就可以进入消息广播模式了。
如果集群中的其他节点收到客户端的事务请求,那么这些非Leader服务器会首先将这个事务请求转发给Leader服务器。Leader服务器会为其生成主要的事务提案Proposal,并将其发送给集群中其余所有的主机,然后再分别收集它们的选票,在选票过半后进行事务提交。其具体过程如下:
Leader接收到请求消息后,消息将赋予一个全局唯一的64位自增id,即zid,通过zxid的大小比较即可实现事务的有序性管理。
为了保证Leader向Follower发送提案的有序,Leader会为每一个Follower创建一个FIFO队列,并将提案副本写入到各个队列,然后通过这些队列将提案发送法给各个Follower。
当Follower接收到提案后,会将提案的zxid与本地记录事务日志中最大的zxid进行比较,若当前提案的zxid大于最大zxid,则将当前提案记录到本地事务日志中,并向Leader返回一个ACK。
当Leader接收到过半的ACKs后,对于之前回复过Leader的Follower,Leader会向其发送COMMIT消息,批准这些Follower在本地执行该消息;对于之前未回复过Leader的Follower,Leader会将这些Follower对应队列中的提案发送给这些Follower,发送的同时会携带COMMIT消息。当Follower接收到COMMIT消息后,就会执行该消息。
恢复模式的两个原则
当集群正在启动过程中,或Leader与超过半数的主机断连后,集群就进入了恢复模式。对于要恢复的数据状态需要遵循两个原则。
已处理过的消息不能丢
当Leader收到超过半数Follower的ACKs后,就向各个Follower广播COMMIT消息,批准各个Server执行该写操作事务。当各个Server接收到Leader的COMMIT消息后就会在本地执行该写操作,然后会向客户端响应写操作成功,但是如果在非全部Follower收到COMMIT消息之前Leader就挂了。这将导致一个结果:部分Server已经执行了该事务,部分Server尚未收到COMMIT消息,所以其并没有执行该事务。当新的Leader被选举出,集群经过恢复模式后需要保证Server上都执行了那些已经被部分Server执行过的事务。
为了保证“已经处理过的消息不能丢”的目的,ZAB的恢复模式使用了以下策略:
选举拥有proposal最大值(即zxid最大)的节点作为新的Leader:由于所有提案被COMMIT之前必须拥有合法数量的Follower ACK,即必须有合法数量的服务器的事务日志上有该提案的proposal,因此,只要有合法数量的节点正常工作,就必然有一个保存了所i有被COMMIT消息的proposal。
新的Leader先将自身拥有而非所有Follower都有的proposal发送给Follower,再将这些proposal的COMMIT命令发送给Follower都保存了并执行了所有的proposal。通过以上策略,能保证已经被处理的消息不会丢。
被丢弃的消息不能再现
当Leader接收到事务请求并生成了proposal,但还未向Follower发送时就挂了。由于其它Follower并没有收到此proposal,即并不知道该proposal的存在,因此在经过恢复模式重新选举产生新的Leader后,这个事务被跳过,在整个集群尚未进入正常服务状态时,之前挂了的Leader主机重新启动并注册成为了Follower。但由于保留了被跳过的proposal,所以其与整个系统的状态是不一致的,需要将该proposal删除。
ZAB通过设计巧妙的zxid实现了这一目的。一个zxid是64位,高32位是纪元epoch编号,每一次选举epoch的值都会增一。这样设计的好处是旧的Leader挂了后重启,它不会被选举为新的Leader,因为此时它的zxid肯定小于当前新的epoch。当旧的Leader作为Follower接入新的Leader后,新的Leader会将其所有的旧的epoch号的未被COMMIT的proposal清除。
Leader选举算法
当集群正在启动过程中,或Leader与超过半数的主机断连后就进入恢复模式。而恢复模式中最重要的阶段就是Leader选举。
在集群过程中的Leader选举过程(算法)与Leader断连后的Leader选举过程稍微有一些区别,基本相同。若进行Leader选举,则至少需要两台主机,这里以三台主机组成的集群为例。
在集群的初始化阶段,当第一台服务器Server1启动时,其会给自己投票,然后发布自己的投票结果。投票包括所推举的服务器的myid和ZXID,使用(myid, ZXID)来表示,此时Server1的投票为(1, 0)。由于其它机器还未启动所以它收不到反馈信息,Server1的状态一直属于Looking,即属于非服务状态。
当第二台服务器Server2启动时,此时两台机器可以相互通信,每台机器试图找到Leader,选举过程如下:
(1)每个Server发出一个投票。此时Server(1,0),Server2的投票为(2,0),然后各自将这个投票发给集群中其它机器。
(2)接受来自各个服务器的投票。集群中每个服务器收到投票后,首先判断该投票的有效性,如检查是否是本轮投票、是否是来自Looking状态的服务器。
(3)处理投票。针对每一个投票,服务器都需要将别人的投票和自己的投票进行PK,PK规则如下:
优先检查ZXID。ZXID比较大的服务器优先作为Leader。
如果ZXID相同,那么就比较myid。myid较大的服务器作为Leader服务器。对于Server1而言,它的投票是(1,0),接收Server2的投票为(2,0)。其首先会比较两者ZXID,均为0,再比较myid,此时Server2的myid最大,于是Server1更新自己的投票为(2,0),然后重新投票,对于Server2而言,其无需更新自己的投票,只是再次向集群中所有主机发送一次投票信息即可。
(4)统计投票。每次投票后,每一台zkServer都会统计投票信息,判断是否已有过半机器接收到相同的投票结果。对于Server1、Server2而言,都统计出集群中已经有两台主机接受了(2,0)的投票信息,此时便认为选出了新的Leader,即Server2。
(5)改变服务器状态。一旦确定了Leader,每个服务器都会更新自己的状态,如果是Follower,那么就变更为FOLLOWING,如果是Leader,就变更为LEADING。
(6)添加主机。在新的Leader选举出来后Server3启动,其想发出新一轮选举。但当前集群中各个主机状态并不是LOOKING,而是各司其职的正常服务,所以其只能是以Follower的身份加入到集群中。
断连后的Leader选举
在ZooKeeper运行期间,Leader与非Leader服务器各司其职,即便当有非Leader服务器宕机或新加入时也不会影响Leader。但是若Leader服务器挂了,那么整个集群将暂停对外服务,进入新一轮的Leader选举,其过程和启动时期的Leader选举过程基本一致。
假设正在运行的有Server1、Server2、Server3三台服务器,当前Leader是Server2,若某一时刻Server2挂了,此时便开始新一轮的Leader选举了。选举过程如下:
(1)变更状态。Leader挂后,余下的非Observer服务器都会将自己的服务器状态由FOLLOWING变更为LOOKING,然后开始进入Leader选举过程。
(2)每个服务器会发出一个投票,任然会先投自己。不过,在运行期间每个服务器上的ZXID可能是不同,此时假定Server1的ZXID为111,Server3的ZXID为333;在第一轮投票中,Server1和Server3都会投自己,产生投票(1,111),(3,333),然后各自将投票发送给集群中所有机器。
(3)接收来自各个服务器的投票。与启动过程相同。集群的每个服务器收到投票后,首先判断该投票是否有效,如检查是否是本轮投票、是否来自LOOKING状态的服务器。
(4)处理投票。与启动过程相同。针对每一个投票,服务器都需要将别人的投票和自己投票进行PK。对于Server1而言,它的投票是(1,111),接收Server3的投票为(3,333)。其首先会比较两者ZXID,Server3的投票的ZXID为333大于Server1投票的ZXID的111,于是Server更新自己的投票为(3,333),然后重新投票。对于Server3而言,其无需更新自己的投票,只是再次向集群中所有主机发出上一次投票信息即可。
(5)统计投票。与启动时过程相同。经过票数统计,最终Server3当选新的Leader。
(6)改变服务器的状态。与启动时过程相同。一旦确定了Leader,每个服务器就会更新自己的状态。Server1变更为FOLLOWING,Server3变更为LEADING。
恢复模式下的数据同步
当完成Leader选举后,就要进入到恢复模式下的数据同步阶段。Leader服务器会为每一个Follower服务器准备一个队列,并将那些没有被各个Follower服务器同步的事务以Proposal的形式逐条发给各个Follower服务器,并在每一个Proposal后紧跟一个COMMIT消息,表示该事务已经被提交,Follower可以直接接收并执行。当Follower服务器将所有尚未同步的事务Proposal都从Leader服务器同步过来并成功执行后,会向准Leader发送ACK信息。Leader服务器在收到该ACK后就会将该Follower加入到真正可用的Follower列表。
CAP原则简介
CAP原则又称CAP定理,指的是在一个分布式系统中,Consistency(一致性)、Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。
一致性(C):分布式系统的所有主机在同一时刻是否可以保证其具有完全相同的数据备份。若具有,则该分布式系统具有一致性。
可用性(A):在集群中部分节点发生故障后,是否会影响对客户端读写请求的响应。注意,若在短时间内会影响,其也不具有这里所说的“可用性”。
分区容错性(P):分布式系统中一台主机称为一个分区。分布式系统中各个主机无法保证数据一致性是一种错误;分布式系统无法及时响应客户端请求,即不具有可用性也是一种错误,对于分布式系统,必须要具有对这些错误的“包容性”,即容错性,这是必须的。
三二原则
对于分布式系统,在CAP原则中分区容错性是必须保证的,但是其并不能同时保证一致性与可用性。因为现在的分布式系统在满足了一致性的前提下,会牺牲可用性;在满足可用性的前提下,会牺牲一致性。所以,CAP原则对于一个分布式系统来说,只能满足两项,即要么CP,要么AP。这就是CAP的三二原则。
ZK与CP
ZK遵循的是CP原则,即保证了一致性,但牺牲了可用性。
当Leader宕机后,ZK集群会马上进行新的Leader选举。但选举时长在30-120秒之间,整个选举期间ZK集群是不接受客户端的读写操作的,即ZK集群处于瘫痪状态。所以,其不满足可用性。
为什么Leader选举需要这么长时间。为了保证ZK集群各个节点中的数据一致性,ZK集群做了两类数据同步:初始化同步与更新同步。当新的Leader被选举出来后,各个Follower需要将新Leader的数据同步到自己的缓存中,这是初始化同步;当Leader的数据被客户端修改后,其会向Follower发出广播,然后各个Follower会主动同步Leader的更新数据,这是更新同步。无论是初始化同步还是更新同步,ZK集群为了保证数据的一致性,若发现超过半数的Follower同步超时,则其会再次进行同步,而这个过程中ZK集群是处于不可用的状态。
由于ZK采用了CP原则,所以导致其可用性降低,这是其致命问题。Spring Cloud的Eureka在分布式系统中所起的作用类似于ZK,但其采用了AP原则,其牺牲了一致性,但是保证可用性。