写在前面:作为初学者而言,个人非常不推荐大家买这本书,如果要读也是读这本书的英文版,里面关于ZooKeeper的应用代码比较多,如果是需要这个可以参考阅读下,其他部分的中文版极其垃圾,机翻明显,语句不通,结论模糊,我用了3-4个晚上读下来收获颇微,所以才有下面这篇借助各位大佬的博客总结了我认为需要了解的ZooKeeper的知识点,每个知识点我都在其中注明了参考链接,如果想详细了解ZooKeeper的知识点不妨点进去更细致地看看,尤其是Zab那一节。
买书的时候记得看看豆瓣的书评,关于ZooKeeper的书应该还有其他的,但是本身没有阅读过就不推荐了,可以参考网上其他人的推荐。

image.png

阅读该文你可以了解到

1.什么是ZooKeeper?
2.ZooKeeper的数据结构是怎么样的?(文件系统树,Node种类)
3.什么是ZooKeeper中的Watcher?(监视器的作用、场景、特点)
4.watcher怎么解决修改丢失?(注册监听器前获取ZooKeeper信息)
5.ZooKeeper的发布和订阅怎么实现?
6.ZookKeeper怎么实现分布式锁?
7.ZooKeeper的统一配置和命名?(5,6,7都是监听节点实现,方法不同)
8.ZooKeeper中的角色(Leader,Follower,Observer)
9.ZooKeeper的一致性保证(写入的顺序一致性和读取的最终一致性)
10.ZooKeeper和CAP理论(分布式系统只能满足一致性、可用性、分区容错性三项中的两项而不可能满足全部三项)
11.ZooKeeper怎么实现事务?(Multiop,可以保证原子性,共同成功/失败)
12.ZooKeeper的curator(一套zookeeper客户端框架,解决了很多Zookeeper客户端非常底层的细节开发工作)
13.ZooKeeper的一致性协议原理Zab(原子消息广播协议,恢复(选举)模式和广播模式)
14.为什么ZooKeeper集群的数目,一般为奇数个?(Paxos协议,大于半数)

1.什么是ZooKeeper?

管理分布式系统的中间件,用来统一配置管理、统一命名服务、分布式锁、集群管理等。

2.ZooKeeper的数据结构是怎么样的?

什么是ZooKeeper?
类似于文件系统的树

image.png

每个节点都是一个Node

Node种类
节点有两个维度,一个是永久的还是临时的,另一个是否有序。组合成的四种类型如下:
1:PERSISTENT // 持久化节点
2:PERSISTENT_SEQUENTIAL // 持久化排序节点
3:EPHEMERAL // 临时节点
4:EPHEMERAL_SEQUENTIAL // 临时排序节点
永久节点:节点创建后会被持久化,只有主动调用delete方法的时候才可以删除节点。
临时节点:节点创建后在创建者超时连接或失去连接的时候,节点会被删除。临时节点下不能存在子节点。
排序节点:创建的节点名称后自动添加序号,如节点名称为"node-",自动添加为"node-1",顺序添加为"node-2"...

3.什么是ZooKeeper中的Watcher?

如下图所示,即监听器

image.png

客户端在向 ZooKeeper 服务器注册 Watcher 的同时,会将 Watcher 对象存储在客户端的 WatchManager 中。当ZooKeeper 服务器触发 Watcher 事件后,会向客户端发送通知,客户端线程从 WatchManager 的实现类中取出对应的 Watcher 对象来执行回调逻辑。

监听器的特点

一次性
无论是服务端还是客户端,一旦一个 Watcher 被触发,ZooKeeper 都会将其从相应的存储中移除。因此,在 Watcher 的使用上,需要反复注册。这样的设计有效地减轻了服务端的压力。

轻量
WatcherEvent 是 ZooKeeper 整个 Watcher 通知机制的最小通知单元,这个数据结构中只包含三部分内容:通知状态、事件类型和节点路径。也就是说,Watcher 通知非常简单,只会告诉客户端发生了事件,而不会说明事件的具体内容。例如针对 NodeDataChanged 事件,ZooKeeper 的Watcher 只会通知客户端指定数据节点的数据内容发生了变更,而对于原始数据以及变更后的新数据都无法从这个事件中直接获取到,而是需要客户端主动重新去获取数据——这也是 ZooKeeper 的 Watcher 机制的一个非常重要的特性

4.watcher怎么解决修改丢失?

试想这么一个问题,由于监视器是单词的,在当前这个监听器触发后,下一个监听器注册前,zookeeper发生了变化怎么办?
这个时候可以通过在注册前再读取一遍zookeeper中的状态信息来实现。用这个方法我们可以做到通知机制更加轻量化,因为可能一次通知我们就可以获得多次修改的内容而不用多次通知。

5.ZooKeeper的发布和订阅怎么实现?

通过监听Znode,可以看他子节点的增减变化,或者它本身的数据变化

6.ZookKeeper怎么实现分布式锁?

image.png

1.客户端连接zookeeper,并在/lock下创建临时的且有序的子节点,第一个客户端对应的子节点为/lock/lock-0000000000,第二个为/lock/lock-0000000001,以此类推。
2.客户端获取/lock下的子节点列表,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如果是则认为获得锁,否则监听刚好在自己之前一位的子节点删除消息,获得子节点变更通知后重复此步骤直至获得锁;
3.执行业务代码;
4.完成业务流程后,删除对应的子节点释放锁。

7.ZooKeeper的统一配置和命名?

我们希望把ASystem.yml、BSystem.yml、CSystem.yml相同的配置项抽取出来成一份公用的配置common.yml,并且即便common.yml改了,也不需要系统A、B、C重启。

image.png

实现统一的命名:
别人访问www.java3y.com即可访问到我的机器,而不是通过IP去访问。

image.png

8.ZooKeeper中的角色

参考:Zookeeper之Leader选举过程
Zookeeper Server三种角色:Leader,Follower,Observer。

Leader是Zookeeper 集群工作机制的核心,主要工作:
调度者:集群内部各个服务节点的调度者
事务请求:事务请求的唯一调度和处理者,保证集群事务处理的顺序性

Follower主要职责:
非事务请求:Follower 直接处理非事务请求,对于事务请求,转发给 Leader
Proposal 投票:Leader 上执行事务时,需要 Follower 投票,Leader 才真正执行
Leader 选举投票

Observer主要职责:
非事务请求:Follower 直接处理非事务请求,对于事务请求,转发给 Leader

Observer 跟 Follower的区别:
Follower 参与投票:Leader 选举、Proposal 提议投票(事务执行确认)
Observer 不参与投票:只用于提供非事务请求的处理

注意:只有写操作需要转给leader处理,然后跑zab, 为了保证consistency,读操作, local端直接解决

9.ZooKeeper的一致性保证

注意:读取并不保证一定读到最新的

image.png

以上图为例,如果一个zk集群有10000台节点,当进行写入的时候,如果已经有6K个节点写入成功,zk就认为本次写请求成功。但是这时候如果一个客户端读取的刚好是另外4K个节点的数据,那么读取到的就是旧的过期数据。

zookeeper一致性的保证:
ZooKeeper是一种高性能,可扩展的服务,虽然读取速度比写入快,但是读取和写入操作都设计的极为快速,这样做的原因是在读取的情况下,ZooKeeper可能会提供较旧的数据,但这是为了ZooKeeper的一致性保证:

顺序一致性:来自客户端的更新将按照发送的顺序被写入到zk

原子性:更新操作要么成功要么失败,没有中间状态
单系统快照:客户端将看到服务的相同视图,而不管它连接到的服务器。

可靠性:一旦应用更新,数据将被持久化,直到数据被再次更新,对于该保证有两个推论:1、如果客户端得到了成功的返回码,说明写入成功,数据被持久化,如果出现了通信错误,超时等一些故障,客户端将不知道更新是否已应用。我们采取措施尽量减少失败,但唯一的保证是只有成功的返回码。 (这在Paxos中称为单调性条件。)2、如果客户端已经读取到了数据或者写入成功了数据,都不会因为zk的失败而导致回滚;

及时性:在一段时间后,客户端将看到最新的系统更新,在此期间客户端将看到这种变更。

10.ZooKeeper和CAP理论

CAP理论
原文出处:Zookeeper并不保证读取的是最新数据
理论计算机科学中,CAP定理(CAP theorem),又被称作布鲁尔定理(Brewer’s theorem),它指出对于一个分布式计算系统来说,不可能同时满足以下三点:

  • 一致性(Consistence) (等同于所有节点访问同一份最新的数据副本)
  • 可用性(Availability)(每次请求都能获取到非错的响应——但是不保证获取的数据为最新数据)
  • 分区容错性(Network partitioning)(以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。)

根据定理,分布式系统只能满足三项中的两项而不可能满足全部三项。理解CAP理论的最简单方式是想象两个节点分处分区两侧。允许至少一个节点更新状态会导致数据不一致,即丧失了C性质。如果为了保证数据一致性,将分区一侧的节点设置为不可用,那么又丧失了A性质。除非两个节点可以互相通信,才能既保证C又保证A,这又会导致丧失P性质

对于zookeeper来说,它实现了A可用性、P分区容错性、C中的写入强一致性,丧失的是C中的读取一致性。

11.ZooKeeper怎么实现事务?

Multiop可以原子性地执行多个Zookeeper的操作,即在multiop代码块中的所有操作要么全部成功,要么全部失败。Multiop并非ZooKeeper的原始设计,该特性在3.4.0版本中被添加进 来

12.ZooKeeper的curator

Curator是Netflix公司开源的一套zookeeper客户端框架,解决了很多Zookeeper客户端非常底层的细节开发工作,包括连接重连、反复注册Watcher和NodeExistsException异常等等。

如果你想找代码实例,下面这个链接会很有用:
Zookeeper客户端Curator使用详解

13.ZooKeeper的一致性协议原理Zab

参考链接如下,如果你想更多的了解这些内容,这三个链接会很有帮助
ZooKeeper学习第七期--ZooKeeper一致性原理
Zookeeper一致性协议原理Zab
深入浅出Zookeeper(一) Zookeeper架构及FastLeaderElection机制

ZAB(ZooKeeper Atomic Broadcast ) 全称为:原子消息广播协议;

ZAB协议的两个基本模式:恢复模式广播模式

恢复模式:(选举)
当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数server完成了和leader的状态同步以后,恢复模式就结束了。状态同步保证了leader和server具有相同的系统状态。

选举模式可能下面这几个场景:
集群启动领导选举
Follower重启
Leader重启
这边主要介绍集群启动领导选举,其他都大同小异

image.png

注意恢复模式下,有两种个保证:
① 我们绝不能遗忘已经被deliver的消息,若一条消息在一台机器上被deliver,那么该消息必须将在每台机器上deliver。
② 我们必须丢弃已经被skip的消息。

尝试理解下以下几个例子,可能在表达上会比较难懂,先来注释几个英文单词的意思:proposal 提议、Quorum 法定人数、deliver 发布、epoch 时期

对于第一种情况:
Leader发送了commit消息,但在该commit消息到达其他任何机器之前,Leader发生了故障。也就是说,只有Leader自己收到了commit消息.

image.png

因为Leader已经deliver了该C2消息,client能够在消息中看到该事务的结果。所以该事务必须能够在其他所有的Server中deliver,最终使得client看到了一个一致性的服务视图。实现方案:通过将新连接上的Follower所没有见过的所有PROPOSAL进行排队,并之后对该Proposals的COMMIT消息进行排队,直到最后一个COMMIT消息。在所有这样的消息已经排好队之后,Leader将会把Follower加入到广播列表,以便今后的提议和确认

对于第二种情况:SKIP
例如,发生了这样一种情况:Leader发送了propose消息,但在该propose消息到达其他任何机器之前,Leader发生了故障。也就是说,只有Leader自己收到了propose消息。
图中没有任何一个server能够看到3号提议,所以在图中当server 1恢复时他需要在系统恢复时丢弃三号提议P3

image.png

skip已经Propose,但不能deliver的消息,处理起来也比较简单。在我们的实现中,Zxid是由64位数字组成的,低32位用作简单计数器。高32位是一个epoch。每当新Leader接管它时,将获取日志中Zxid最大的epoch,新Leader Zxid的epoch位设置为epoch+1,counter位设置0。用epoch来标记领导关系的改变,并要求Quorum Servers 通过epoch来识别该leader,避免了多个Leader用同一个Zxid发布不同的提议。

这 个方案的一个优点就是,我们可以skip一个失败的领导者的实例,从而加速并简化了恢复过程。如果一台宕机的Server重启,并带有未发布的 Proposal,那么先前的未发布的所有提议将永不会被deliver。并且它不能够成为一个新leader,因为任何一种可能的 Quorum Servers ,都会有一个Server其Proposal 来自与一个新epoch,并且这个Proposal对应的Zxid低于这个已经失败的leader,当Server以Follower的身份连接,领导者检查自身最后提交的提议,该提议的epoch 为Follower的最新提议的epoch(也就是图中新Leader-Server2中deliver的C2提议),并告诉Follower截断 事务日志直到该epoch在新Leader中deliver的最后的Proposal即C2。在图中,当旧Leader-Server1连接到了新leader-Server2,leader将告诉他从事务日志中清除3号提议P3,具体点就是清除P2之后的所有提议,因为P2之后的所有提议只有旧Leader-Server1知道,其他Server不知道。

广播模式:(数据同步)
一旦Leader已经和多数的Follower进行了状态同步后,他就可以开始广播消息了,即进入广播状态。
这时候当一个Server加入ZooKeeper服务中,它会在恢复模式下启动,发现Leader,并和Leader进行状态同步。待到同步结束,它也参与消息广播。
ZooKeeper服务一直维持在广播状态,直到Leader崩溃了或者Leader失去了大部分的Followers支持。

广播模式极其类似于分布式事务中的2pc(two-phrase commit 两阶段提交):即Leader提起一个决议,由Followers进行投票,Leader对投票结果进行计算决定是否通过该决议,如果通过执行该决议(事务),否则什么也不做。

14.为什么ZooKeeper集群的数目,一般为奇数个?

Leader选举算法采用了Paxos协议;
Paxos核心思想:当超过半数Server写成功,则任务数据写成功。如果有3个Server,则两个写成功即可;如果有4或5个Server,则三个写成功即可。注意是大于不是大于等于半数
Server数目一般为奇数(3、5、7),如果有3个Server,则最多允许1个Server挂掉;如果有4个Server,则同样最多允许1个Server挂掉由此,
我们看出3台服务器和4台服务器的的容灾能力是一样的,所以为了节省服务器资源,一般我们采用奇数个数,作为服务器部署个数。

作者:就这些吗
链接:https://www.jianshu.com/p/02b1ca8615dc
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

发表评论

邮箱地址不会被公开。 必填项已用*标注