ZooKeeper 是一个开源的分布式协调服务。它是一个为分布式应用提供一致性服务的软件,分布式应用程序可以基于 Zookeeper 实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列等功能
有序性是 zookeeper 中非常重要的一个特性,所有的更新都是全局有序的,每个更新都有一个唯一的时间戳,这个时间戳称为 zxid(Zookeeper Transaction Id)。而读请求只会相对于更新有序,也就是读请求的返回结果中会带有这个zookeeper 最新的 zxid
Zookeeper 对于读写请求有所不同:
- 客户端的读请求可以被集群中的任意一台机器处理,如果读请求在节点上注册了监听器,这个监听器也是由所连接的 Zookeeper 机器来处理。
- 对于写请求,这些请求会同时发给其他 Zookeeper 机器并且达成一致后,请求才会返回成功。因此,随着 Zookeeper 的集群机器增多,读请求的吞吐会提高但是写请求的吞吐会下降。
🦅 Chubby 是什么?和 Zookeeper 对比你怎么看?
- Chubby 是 Google 的,完全实现 Paxos 算法,不开源。
- Zookeeper 是 Chubby 的开源实现,使用 ZAB 协议(Paxos 算法的变种)。
🦅 Zookeeper 的 Java 客户端都有哪些?
- Zookeeper 自带的 zkclient
-
Apache 开源的 Curator
watcher监听不是永久的,watcher触发后,zookeeper会将其从相应的存储中删除
节点删除,watch就会触发对应的事件
3. Zookeeper 文件系统
Zookeeper 提供一个多层级的节点命名空间(节点称为 znode)。与文件系统不同的是,这些节点都可以设置关联的数据,而文件系统中只有文件节点可以存放数据而目录节点不行。
Zookeeper 为了保证高吞吐和低延迟,在内存中维护了这个树状的目录结构,这种特性使得 Zookeeper 不能用于存放大量的数据,每个节点的存放数据上限为1M
5. 四种类型的数据节点 Znode
(1)PERSISTENT-持久节点
除非手动删除,否则节点一直存在于 Zookeeper 上
(2)EPHEMERAL-临时节点
临时节点的生命周期与客户端会话绑定,一旦客户端会话失效(客户端与zookeeper 连接断开不一定会话失效),那么这个客户端创建的所有临时节点都会被移除。
(3)PERSISTENT_SEQUENTIAL-持久顺序节点
基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。
(4)EPHEMERAL_SEQUENTIAL-临时顺序节点
基本特性同临时节点,增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。
6. Zookeeper Watcher 机制 – 数据变更通知
Zookeeper 允许客户端向服务端的某个 Znode 注册一个 Watcher 监听,当服务端的一些指定事件触发了这个 Watcher,服务端会向指定客户端发送一个事件通知来实现分布式的通知功能,然后客户端根据 Watcher 通知状态和事件类型做出业务上的改变。
工作机制:
(1)客户端注册 watcher
(2)服务端处理 watcher
(3)客户端回调 watcher
3.2、客户端向服务端注册 Watcher 的时候,并不会把客户端真实的 Watcher 对象实体传递到服务端,仅仅是在客户端请求中使用 boolean 类型属性进行了标记。
(4)watcher event 异步发送 watcher 的通知事件从 server 发送到 client 是异步的,这就存在一个问题,不同的客户端和服务器之间通过 socket 进行通信,由于网络延迟或其他因素导致客户端在不通的时刻监听到事件,由于 Zookeeper 本身提供了 ordering guarantee,即客户端监听事件后,才会感知它所监视 znode发生了变化。所以我们使用 Zookeeper 不能期望能够监控到节点每次的变化。Zookeeper 只能保证最终的一致性,而无法保证强一致性。
9. 客户端回调 Watcher
客户端 SendThread 线程接收事件通知,交由 EventThread 线程回调 Watcher。
客户端的 Watcher 机制同样是一次性的,一旦被触发后,该 Watcher 就失效了
14. Zookeeper 下 Server 工作状态
服务器具有四种状态,分别是 LOOKING、FOLLOWING、LEADING、OBSERVING。
(1)LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。
(2)FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。
(3)LEADING:领导者状态。表明当前服务器角色是 Leader。
(4)OBSERVING:观察者状态。表明当前服务器角色是 Observer。
15. 数据同步
整个集群完成 Leader 选举之后,Learner(Follower 和 Observer 的统称)回向Leader 服务器进行注册。当 Learner 服务器想 Leader 服务器完成注册后,进入数据同步环节
16. zookeeper 是如何保证事务的顺序一致性的?
zookeeper 采用了全局递增的事务 Id 来标识,所有的 proposal(提议)都在被提出的时候加上了 zxid,zxid 实际上是一个 64 位的数字,高 32 位是 epoch( 时期; 纪元; 世; 新时代)用来标识 leader 周期,如果有新的 leader 产生出来,epoch会自增,低 32 位用来递增计数。当新产生 proposal 的时候,会依据数据库的两阶段过程,首先会向其他的 server 发出事务执行请求,如果超过半数的机器都能执行并且能够成功,那么就会开始执行
17. 分布式集群中为什么会有 Master主节点?
在分布式环境中,有些业务逻辑只需要集群中的某一台机器进行执行,其他的机器可以共享这个结果,这样可以大大减少重复计算,提高性能,于是就需要进行 leader 选举。
chubby 是 google 的,完全实现 paxos 算法,不开源。zookeeper 是 chubby的开源实现,使用 zab 协议,paxos 算法的变种。
28. Zookeeper 的典型应用场景
Zookeeper 是一个典型的发布/订阅模式的分布式数据管理与协调框架,开发人员可以使用它来进行分布式数据的发布和订阅。
通过对 Zookeeper 中丰富的数据节点进行交叉使用,配合 Watcher 事件通知机制,可以非常方便的构建一系列分布式应用中年都会涉及的核心功能,如:
(1)数据发布/订阅
(2)负载均衡
(3)命名服务
(4)分布式协调/通知
(5)集群管理
(6)Master 选举
(7)分布式锁
(8)分布式队列
zookeeper和dubbo的关系:
Dubbo 的将注册中心进行抽象,它可以外接不同的存储媒介给注册中心提供服务,有 ZooKeeper,Memcached,Redis 等。
引入了 ZooKeeper 作为存储媒介,也就把 ZooKeeper 的特性引进来。首先是负载均衡,单注册中心的承载能力是有限的,在流量达到一定程度的时 候就需要分流,负载均衡就是为了分流而存在的,一个 ZooKeeper 群配合相应的 Web 应用就可以很容易达到负载均衡;资源同步,单单有负载均衡还不够,节点之间的数据和资源需要同步,ZooKeeper 集群就天然具备有这样的功能;命名服务,将树状结构用于维护全局的服务地址列表,服务提供者在启动 的时候,向 ZooKeeper 上的指定节点 /dubbo/${serviceName}/providers 目录下写入自己的 URL 地址,这个操作就完成了服务的发布。 其他特性还有 Master 选举,分布式锁等。
18.zookeeper负载均衡和nginx负载均衡区别
zookeeper
不存在单点问题,zab机制保证单点故障可重新选举一个leader
只负责服务的注册与发现,不负责转发,减少一次数据交换(消费方与服务方直接通信)
需要自己实现相应的负载均衡算法
nginx
存在单点问题,单点负载高数据量大,需要通过KeepAlived+LVS备机实现高可用
每次负载,都充当一次中间人转发角色,增加网络负载量(消费方与服务方间接通信)
自带负载均衡算法
🦅 集群中的机器角色有哪些?
集群中一共有三种角色:
-
1、Leader
-
-
Zookeeper 的选举过程?
当 Leader 崩溃,或者 Leader 失去大多数的 Follower,这时 Zookeeper 进入恢复模式,恢复模式需要重新选举出一个新的 Leader,让所有的 Server 都恢复到一个正确的状态。
Zookeeper 的选举算法有两种:一种是基于 basic paxos 实现的,另外一种是基于 fast paxos 算法实现的。系统默认的选举算法为 fast paxos 。
FastLeaderElection选举算法
由于LeaderElection收敛速度较慢,所以Zookeeper引入了FastLeaderElection选举算法,FastLeaderElection也成了Zookeeper默认的Leader选举算法。
FastLeaderElection是标准的Fast Paxos的实现,它首先向所有Server提议自己要成为leader,当其它Server收到提议以后,解决 epoch 和 zxid 的冲突,并接受对方的提议,然后向对方发送接受提议完成的消息。FastLeaderElection算法通过异步的通信方式来收集其它节点的选票,同时在分析选票时又根据投票者的当前状态来作不同的处理,以加快Leader的选举进程。
算法流程
数据恢复阶段
每个ZooKeeper Server读取当前磁盘的数据(transaction log),获取最大的zxid。
发送选票
每个参与投票的ZooKeeper Server向其他Server发送自己所推荐的Leader,这个协议中包括几部分数据:
- 所推举的Leader id。在初始阶段,第一次投票所有Server都推举自己为Leader。
- 本机的最大zxid值。这个值越大,说明该Server的数据越新。
- logicalclock。这个值从0开始递增,每次选举对应一个值,即在同一次选举中,这个值是一致的。这个值越大说明选举进程越新。
- 本机的所处状态。包括LOOKING,FOLLOWING,OBSERVING,LEADING。
处理选票
每台Server将自己的数据发送给其他Server之后,同样也要接受其他Server的选票,并做一下处理。
如果Sender的状态是LOOKING
- 如果发送过来的logicalclock大于目前的logicalclock。说明这是更新的一次选举,需要更新本机的logicalclock,同事清空已经收集到的选票,因为这些数据已经不再有效。然后判断是否需要更新自己的选举情况。首先判断zxid,zxid大者胜出;如果相同比较leader id,大者胜出。
- 如果发送过来的logicalclock小于于目前的logicalclock。说明对方处于一个比较早的选举进程,只需要将本机的数据发送过去即可。
- 如果发送过来的logicalclock等于目前的logicalclock。根据收到的zxid和leader id更新选票,然后广播出去。
当Server处理完选票后,可能需要对Server的状态进行更新:
- 判断服务器是否已经收集到所有的服务器的选举状态。如果是根据选举结果设置自己的角色(FOLLOWING or LEADER),然后退出选举。
- 如果没有收到没有所有服务器的选举状态,也可以判断一下根据以上过程之后更新的选举Leader是不是得到了超过半数以上服务器的支持。如果是,那么尝试在200ms内接收下数据,如果没有心数据到来说明大家已经认同这个结果。这时,设置角色然后退出选举。
如果Sender的状态是FOLLOWING或者LEADER
- 如果LogicalClock相同,将数据保存早recvset,如果Sender宣称自己是Leader,那么判断是不是半数以上的服务器都选举它,如果是设置角色并退出选举。
- 否则,这是一条与当前LogicalClock不符合的消息,说明在另一个选举过程中已经有了选举结果,于是将该选举结果加入到OutOfElection集合中,根据OutOfElection来判断是否可以结束选举,如果可以也是保存LogicalClock,更新角色,退出选举。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
zookeeper如何保证数据一致性?剖析Zap协议
ZooKeeper服务有两种不同的运行模式。一种是"独立模式"(standalone mode),即只有一个ZooKeeper服务器。这种模式较为简单,比较适合于单元测试、测试环境中采用,但是不能保证高可用性和恢复性。在生产环境中的ZooKeeper通常以"复制模式"(replicated mode)运行于一个计算机集群上,这个计算机集群被称为一个"集合体"(ensemble)。保证ZooKeeper服务的高可用性需要"复制模式",通过写多份来冗余数据,那么如何解决写多份带来一致性问题呢?如何解决一致性问题又会带来性能问题?
deliver 投递
选举出新的leader后,会先检查事务日志总是否存在未提交,且在超过半数节点中存在的消息P1,P2;并且依次发出C1,C2将P1,P2提交
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
选举新的leader,然后和集群中的follower服务器开始数据同步。把没提交的,超过半数的事件提交后; leader服务器再接收客户端的请求,再生成事务提案进行事务请求处理。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
数据节点(dataNode):zk数据模型中的最小数据单元,数据模型是一棵树,由斜杠(/)分割的路径名唯一标识,数据节点可以存储数据内容及一系列属性信息,同时还可以挂载子节点,构成一个层次化的命名空间。
-
会话(Session):指zk客户端与zk服务器之间的会话,在zk中,会话是通过客户端和服务器之间的一个TCP长连接来实现的。通过这个长连接,客户端能够使用心跳检测与服务器保持有效的会话,也能向服务器发送请求并接收响应,还可接收服务器的Watcher事件通知。Session的sessionTimeout,是会话超时时间,如果这段时间内,客户端未与服务器发生任何沟通(心跳或请求),服务器端会清除该session数据,客户端的TCP长连接将不可用,这种情况下,客户端需要重新实例化一个Zookeeper对象。
-
事务及ZXID:事务是指能够改变Zookeeper服务器状态的操作,一般包括数据节点的创建与删除、数据节点内容更新和客户端会话创建与失效等操作。对于每个事务请求,zk都会为其分配一个全局唯一的事务ID,即ZXID,是一个64位的数字,高32位表示该事务发生的集群选举周期(集群每发生一次leader选举,值加1),低32位表示该事务在当前选择周期内的递增次序(leader每处理一个事务请求,值加1,发生一次leader选择,低32位要清0)。
-
事务日志:所有事务操作都是需要记录到日志文件中的,可通过 dataLogDir配置文件目录,文件是以写入的第一条事务zxid为后缀,方便后续的定位查找。zk会采取“磁盘空间预分配”的策略,来避免磁盘Seek频率,提升zk服务器对事务请求的影响能力。默认设置下,每次事务日志写入操作都会实时刷入磁盘,也可以设置成非实时(写到内存文件流,定时批量写入磁盘),但那样断电时会带来丢失数据的风险。
-
数据快照:数据快照是zk数据存储中另一个非常核心的运行机制。数据快照用来记录zk服务器上某一时刻的全量内存数据内容,并将其写入到指定的磁盘文件中,可通过dataDir配置文件目录。可配置参数snapCount,设置两次快照之间的事务操作个数,zk节点记录完事务日志时,会统计判断是否需要做数据快照(距离上次快照,事务操作次数等于snapCount/2~snapCount 中的某个值时,会触发快照生成操作,随机值是为了避免所有节点同时生成快照,导致集群影响缓慢)。
-
过半:所谓“过半”是指大于集群机器数量的一半,即大于或等于(n/2+1),此处的“集群机器数量”不包括observer角色节点。leader广播一个事务消息后,当收到半数以上的ack信息时,就认为集群中所有节点都收到了消息,然后leader就不需要再等待剩余节点的ack,直接广播commit消息,提交事务。选举中的投票提议及数据同步时,也是如此,leader不需要等到所有learner节点的反馈,只要收到过半的反馈就可进行下一步操作。
二、数据模型
zk维护的数据主要有:客户端的会话(session)状态及数据节点(dataNode)信息。zk在内存中构造了个DataTree的数据结构,维护着path到dataNode的映射以及dataNode间的树状层级关系。为了提高读取性能,集群中每个服务节点都是将数据全量存储在内存中。可见,zk最适于读多写少且轻量级数据(默认设置下单个dataNode限制为1MB大小)的应用场景。数据仅存储在内存是很不安全的,zk采用事务日志文件及快照文件的方案来落盘数据,保障数据在不丢失的情况下能快速恢复。
zk集群由多个节点组成,其中有且仅有一个leader,处理所有事务请求;follower及observer统称learner。learner需要同步leader的数据。follower还参与选举及事务决策过程。zk客户端会打散配置文件中的serverAddress 顺序并随机组成新的list,然后循环按序取一个服务器地址进行连接,直到成功。follower及observer会将事务请求转交给leader处理。
四、zookeeper一致性协议
zookeeper实现数据一致性的核心是ZAB协议(Zookeeper原子消息广播协议)。该协议需要做到以下几点:
(1)集群在半数以下节点宕机的情况下,能正常对外提供服务;
(2)客户端的写请求全部转交给leader来处理,leader需确保写变更能实时同步给所有follower及observer;
(3)leader宕机或整个集群重启时,需要确保那些已经在leader服务器上提交的事务最终被所有服务器都提交,确保丢弃那些只在leader服务器上被提出的事务,并保证集群能快速恢复到故障前的状态。
Zab协议有两种模式, 崩溃恢复(选主+数据同步)和消息广播(事务操作)。任何时候都需要保证只有一个主进程负责进行事务操作,而如果主进程崩溃了,就需要迅速选举出一个新的主进程。主进程的选举机制与事务操作机制是紧密相关的。下面详细讲解这三个场景的协议规则,从细节去探索ZAB协议的数据一致性原理。
1、选主:leader选举是zk中最重要的技术之一,也是保证分布式数据一致性的关键所在。当集群中的一台服务器处于如下两种情况之一时,就会进入leader选举阶段——服务器初始化启动、服务器运行期间无法与leader保持连接。
循环等待流程中,节点每收到一个外部的Vote信息,都需要将其与自己内存Vote数据进行PK,规则为取ZXID大的,若ZXID相等,则取ID大的那个投票。若外部投票胜选
2、选主后的数据同步:选主算法中的zxid是从内存数据库中取的最新事务id,事务操作是分两阶段的(提出阶段和提交阶段),leader生成提议并广播给followers,收到半数以上的ACK后,再广播commit消息,同时将事务操作应用到内存中。follower收到提议后先将事务写到本地事务日志,然后反馈ACK,等接到leader的commit消息时,才会将事务操作应用到内存中。可见,选主只是选出了内存数据是最新的节点,仅仅靠这个是无法保证已经在leader服务器上提交的事务最终被所有服务器都提交。比如leader发起提议P1,并收到半数以上follower关于P1的ACK后,在广播commit消息之前宕机了,选举产生的新leader之前是follower,未收到关于P1的commit消息,内存中是没有P1的数据。而ZAB协议的设计是需要保证选主后,P1是需要应用到集群中的。这块的逻辑是通过选主后的数据同步来弥补。
选主后,节点需要切换状态,leader切换成LEADING状态后的流程如下:
(1)重新加载本地磁盘上的数据快照至内存,并从日志文件中取出快照之后的所有事务操作,逐条应用至内存,并添加到已提交事务缓存commitedProposals。这样能保证日志文件中的事务操作,必定会应用到leader的内存数据库中。
(2)获取learner发送的FOLLOWERINFO/OBSERVERINFO信息,并与自身commitedProposals比对,确定采用哪种同步方式,不同的learner可能采用不同同步方式(DIFF同步、TRUNC+DIFF同步、SNAP同步)。这里是拿learner内存中的zxid与leader内存中的commitedProposals(min、max)比对,如果zxid介于min与max之间,但又不存在于commitedProposals中时,说明该zxid对应的事务需要TRUNC回滚;如果 zxid 介于min与max之间且存在于commitedProposals中,则leader需要将zxid+1~max 间所有事务同步给learner,这些内存缺失数据,很可能是因为leader切换过程中造成commit消息丢失,learner只完成了事务日志写入,未完成提交事务,未应用到内存。
(3)leader主动向所有learner发送同步数据消息,每个learner有自己的发送队列,互不干扰。同步结束时,leader会向learner发送NEWLEADER指令,同时learner会反馈一个ACK。当leader接收到来自learner的ACK消息后,就认为当前learner已经完成了数据同步,同时进入“过半策略”等待阶段。当leader统计到收到了一半已上的ACK时,会向所有已经完成数据同步的learner发送一个UPTODATE指令,用来通知learner集群已经完成了数据同步,可以对外服务了。
细节可参照Leader.lead() 、Follower.followLeader()及LearnerHandler类。
ZAB协议的二阶段提交过程中,移除了中断逻辑(事务回滚),所有follower服务器要么正常反馈leader提出的事务proposal,要么就抛弃leader服务器。follower收到proposal后的处理很简单,将该proposal写入到事务日志,然后立马反馈ACK给leader,也就是说如果不是网络、内存或磁盘等问题,follower肯定会写入成功,并正常反馈ACK。leader收到过半follower的ACK后,会广播commit消息给所有learner,并将事务应用到内存;learner收到commit消息后会将事务应用到内存。
ZAB协议中多次用到“过半”设计策略 ,该策略是zk在A(可用性)与C(一致性)间做的取舍,也是zk具有高容错特性的本质。相较分布式事务中的2PC(二阶段提交协议)的“全量通过”,ZAB协议可用性更高(牺牲了部分一致性),能在集群半数以下服务宕机时正常对外提供服务。
ZAB协议,过半就可以提交;比2pc可用性更高,但是也牺牲了部分一致性
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
一、ZooKeeper权限管理机制
1.1 权限管理ACL(Access Control List)
ZooKeeper 的权限管理亦即ACL 控制功能,使用ACL来对Znode进行访问控制。ACL的实现和Unix文件访问许可非常相似:它使用许可位来对一个节点的不同操作进行允许或禁止的权 限控制。但是和标准的Unix许可不同的是,Zookeeper对于用户类别的区分,不止局限于所有者(owner)、组 (group)、所有人(world)三个级别。Zookeeper中,数据节点没有"所有者"的概念。访问者利用id标识自己的身份,并获得与之相应的 不同的访问权限。
ZooKeeper 的权限管理通过Server、Client 两端协调完成:
(2) 客户端
Client 通过调用addAuthInfo()函数设置当前会话的Author信息(针对Digest 验证模式)。Server 收到Client 发送的操作请求(除exists、getAcl 之外),需要进行ACL 验证:对该请求携带的Author 明文信息加密,并与目标节点的ACL 信息进行比较,如果匹配则具有相应的权限,否则请求被Server 拒绝。
(4) 关于ZooKeeper"监视"机制的注意点:
① "监视点"是一次性的。
② 由于"监视点"是一次性的,而且,从接收到"监视"事件到设置新"监视点"是有延时的,所以客户端可能监控不到数据的所有变化。
③ 一个监控对象,只会被相关的通知触发一次。如果一个客户端设置了关于某个数据点exists和getData的监控,则当该数据被删除的时候,只会触发"文件被删除"的
通知。
④ 当客户端断开与服务器的连接时,客户端不再能收到"监视"事件,直到重新获得连接。所以关于Session的信息将被发送给所有ZooKeeper服务器。由于当连接断开时收不到"监视",所以在这种情况下,模块行为需要容错方面的设计。
通过注册观察对象,使用了ZooKeeper对象的客户端可以收到状态转换通知。在进入CONNECTED状态时,观察对象会收到一个WatchedEvent通知,其中KeeperState的值是SyncConnected。
(2) Watch与ZooKeeper状态
ZooKeeper的观察对象肩负着双重责任:
① 可以用来获得ZooKeeper状态变化的相关通知;
② 可以用来获得Znode变化的相关通知。
监视ZooKeeper状态变化:可以使用ZooKeeper对象默认构造函数的观察。
监视Znode变化:可以使用一个专用的观察对象,将其传递给适当的读操作。也可以通过读操作中的布尔标识来设定是否共享使用默认的观察。
ZooKeeper实例可能失去或重新连接ZooKeeper服务,在CONNECTED和CONNECTING状态中切换。如果连接断 开,watcher得到一个Disconnected事件。学要注意的是,这些状态的迁移是由ZooKeeper实例自己发起的,如果连接断开他将自动尝 试自动连接。
如果任何一个close()方法被调用,或是会话由Expired类型的KeepState提示过期时,ZooKeeper可能会转变成第三种状态 CLOSED。一旦处于CLOSED状态,ZooKeeper对象将不再是活动的了(可以使用states的isActive()方法进行测试),而且不 能被重用。客户端必须建立一个新的ZooKeeper实例才能重新连接到ZooKeeper服务。
Zookeeper是一个由多个Server组成的集群,该集群有一个Leader,多个Follower。客户端可以连接任意ZooKeeper服务节点来读写数据
(2) 一致性分类
一致性是指从系统外部读取系统内部的数据时,在一定约束条件下相同,即数据变动在系统内部各节点应该是同步的。根据一致性的强弱程度不同,可以将一致性级别分为如下几种:
① 强一致性(strong consistency)。任何时刻,任何用户都能读取到最近一次成功更新的数据。
② 单调一致性(monotonic consistency)。任何时刻,任何用户一旦读到某个数据在某次更新后的值,那么就不会再读到比这个值更旧的值。也就是说,可获取的数据顺序必是单调递增的。
③ 会话一致性(session consistency)。任何用户在某次会话中,一旦读到某个数据在某次更新后的值,那么在本次会话中就不会再读到比这个值更旧的值。会话一致性是在单调一致性的基础上进一步放松约束,只保证单个用户单个会话内的单调性,在不同用户或同一用户不同会话间则没有保障。
④ 最终一致性(eventual consistency)。用户只能读到某次更新后的值,但系统保证数据将最终达到完全一致的状态,只是所需时间不能保障。
⑤ 弱一致性(weak consistency)。用户无法在确定时间内读到最新更新的值。
2.2 ZooKeeper与CAP理论
我们知道ZooKeeper也是一种分布式系统,它在一致性上有人认为它提供的是一种强一致性的服务(通过sync操作),也有人认为是单调一致性(更新时的大多说概念),还有人为是最终一致性(顺序一致性),反正各有各的道理这里就不在争辩了。然后它在分区容错性和可用性上做了一定折中,这和CAP理论是吻合的。ZooKeeper从以下几点保证了数据的一致性
① 顺序一致性
来自任意特定客户端的更新都会按其发送顺序被提交。也就是说,如果一个客户端将Znode z的值更新为a,在之后的操作中,它又将z的值更新为b,则没有客户端能够在看到z的值是b之后再看到值a(如果没有其他对z的更新)。
② 原子性
每个更新要么成功,要么失败。这意味着如果一个更新失败,则不会有客户端会看到这个更新的结果。
③ 单一系统映像
一 个客户端无论连接到哪一台服务器,它看到的都是同样的系统视图。这意味着,如果一个客户端在同一个会话中连接到一台新的服务器,它所看到的系统状态不会比 在之前服务器上所看到的更老。当一台服务器出现故障,导致它的一个客户端需要尝试连接集合体中其他的服务器时,所有滞后于故障服务器的服务器都不会接受该 连接请求,除非这些服务器赶上故障服务器。
④ 持久性
一个更新一旦成功,其结果就会持久存在并且不会被撤销。这表明更新不会受到服务器故障的影响。
在广播模式ZooKeeper Server会接受Client请求,所有的写请求都被转发给领导者,再由领导者将更新广播给跟随者。当半数以上的跟随者已经将修改持久化之后,领导者才会提交这个更新,然后客户端才会收到一个更新成功的响应。这个用来达成共识的协议被设计成具有原子性,因此每个修改要么成功要么失败。
3.2 Zab协议详解
3.2.1 广播模式
广播模式类似一个简单的两阶段提交:Leader发起一个请求,收集选票,并且最终提交,图3.3演示了我们协议的消息流程。我们可以简化该两阶段提交协议,因为我们并没有"aborts"的情况。followers要么确认Leader的Propose,要么丢弃该Leader的Propose。没有"aborts"意味着,只要有指定数量的机器确认了该Propose,而不是等待所有机器的回应。
3.2.2 恢复模式
(1) 恢复阶段概述
正常工作时Zab协议会一直处于广播模式,直到Leader故障或失去了指定数量的Followers。 为了保证进度,恢复过程中必须选举出一个新Leader,并且最终让所有的Server拥有一个正确的状态。对于Leader选举,需要一个能够成功高几 率的保证存活的算法。Leader选举协议,不仅能够让一个Leader得知它是leader,并且有指定数量的Follower同意该决定。如果 Leader选举阶段发生错误,那么Servers将不会取得进展。最终会发生超时,重新进行Leader选举。在我们的实现中,Leader选举有两种不同的实现方式。如果有指定数量的Server正常运行,快速选举的完成只需要几百毫秒。
其他机器也必须要deliverC2消息,也就是要完成C2的提交;因为P2作为一个proposal已经得到了大多数机器的ack确认,所以C2需要在follower机器上得到执行。
如果leader此时发起了一个P3提议,但是server2和server3根本就没有收到这个P3提议;所以此时server1恢复后,P3就要被丢弃
Paxos的一致性不能达到ZooKeeper的要求
② Zab一致性
首先来分析一下,上面的示例中为什么不满足ZooKeeper需求。ZooKeeper是一个树形结构,很多操作都要先检查才能确定能不能执行,比如,在图3.8中Server2有三条Proposal。P1的事务是创建节点"/zk",P2'是创建节点"/a",而P3是创建节点"/a/b",由于"/a"还没建,创建"a/b"就搞不定了。那么,我们就能从此看出Paxos的一致性达不到ZooKeeper一致性的要求。
为了达到ZooKeeper所需要的一致性,ZooKeeper采用了Zab协议。Zab做了如下几条保证,来达到ZooKeeper要求的一致性。
(a) Zab要保证同一个leader的发起的事务要按顺序被apply,同时还要保证只有先前的leader的所有事务都被apply之后,新选的leader才能在发起事务。
(b) 一些已经Skip的消息,需要仍然被Skip。
我想对于第一条保证大家都能理解,它主要是为了保证每 个Server的数据视图的一致性。我重点解释一下第二条,它是如何实现。为了能够实现,Skip已经被skip的消息。我们在Zxid中引入了 epoch,如下图所示。每当Leader发生变换时,epoch位就加1,counter位置0。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++