额,我是按照书中的顺序写的。mybatis之后就是zookeeper。很巧,我前一段时间正好也在用dubbo+zookeeper来写微服务。所以这也算是加深我对zookeeper的了解和理解了。顺达打广告java技术交流群130031711,群文件中有大量的学习资料,欢迎各位萌新大佬踊跃加入!
接下来闲话少叙,我直接列出题目:
1.Zookeeper是什么?
ZooKeeper 是一个开放源码的分布式协调服务,它是集群的管理者,监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操作。最终,将简单易用的接口和性能高效、功能稳定的系统提供给用户。
分布式应用程序可以基于 Zookeeper 实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列等功能。
以上是书中的答案。其实这个哪怕是按照自己的理解也是可以说好久的吧。分布式协调服务。我之前习惯性的认为zookeeper只是一个注册中心。负责发布订阅等。但是其实这个注册中心也是有需多功能的。我经常喜欢用淘宝来比喻注册中心,然后提供者是商家,消费者是买家。确实本质上淘宝只是个平台。但是这个平台对于店铺开关的审核不就好像是发布?对于店铺的推荐不就好像负载均衡?同样其实命名,协调通知之类的,其实很容易就能理解。
至于集群管理,master选举,分布式锁等是要体现在多个zookeeper协调工作的情况下。对于master选举后面会有详细的介绍。反正整体的用处和功能有个认识就好,下一题。
2. Zookeeper集群是不是性能一定会提升?
答案是否定的。
Zookeeper 集群保证了如下分布式一致性特性:
1、顺序一致性
2、原子性
3、单一视图
4、可靠性
5、实时性(最终一致性)
客户端的读请求可以被集群中的任意一台机器处理,如果读请求在节点上注册了监听器,这个监听器也是由所连接的 zookeeper 机器来处理。对于写请求,这些请求会同时发给其他 zookeeper 机器并且达成一致后,请求才会返回成功。因此,随着zookeeper 的集群机器增多,读请求的吞吐会提高但是写请求的吞吐会下降。
有序性是 zookeeper 中非常重要的一个特性,所有的更新都是全局有序的,每个更新都有一个唯一的时间戳,这个时间戳称为 zxid(Zookeeper Transaction Id)。而读请求只会相对于更新有序,也就是读请求的返回结果中会带有这个zookeeper 最新的 zxid。
3. ZAB 协议?
ZAB 协议是为分布式协调服务 Zookeeper 专门设计的一种支持崩溃恢复的原子广播协议。
ZAB 协议包括两种基本的模式:崩溃恢复和消息广播。
当整个 zookeeper 集群刚刚启动或者 Leader 服务器宕机、重启或者网络故障导致不存在过半的服务器与 Leader 服务器保持正常通信时,所有进程(服务器)进入崩溃恢复模式,首先选举产生新的 Leader 服务器,然后集群中 Follower 服务器开始与新的 Leader 服务器进行数据同步,当集群中超过半数机器与该 Leader服务器完成数据同步之后,退出恢复模式进入消息广播模式,Leader 服务器开始接收客户端的事务请求生成事物提案来进行事务请求处理。
以上内容我不知道你们是怎么理解的,但是我之前认真研究eureka的时候,了解了一个很有意思的概念:就是eureka的自我保护机制。
很巧这段也是我当年写的东西。两者的区别的这里的自我保护指eureka作为注册中心而来着注册服务的作为另一方。小声BB一句:eureka的自我保护机制默认阈值0.85。
而这里的崩溃恢复是指zookeeper集群,其中扮演注册中心角色的是Leader,而扮演注册服务的是其余的服务器。
这里定义如果超过一半的服务器与Leader无法通信则认为是Leader出错了,所以要选举新的Leader,然后Follower服务器挨个去向新Leader注册。超过一半的通信建立了则认为现在集群可以正常使用了,然后重新接收客户端传来请求。
这个我觉得挺好理解的,至于这个崩溃恢复和消息广播两个术语自己记一下吧。我其实也是第一次听说(毕竟现在zookeeper也只是简单的使用,都没用过集群的孩子伤不起~)
4. Zookeeper Watcher 机制的实现原理?
Zookeeper 允许客户端向服务端的某个 Znode 注册一个 Watcher 监听,当服务端的一些指定事件触发了这个 Watcher,服务端会向指定客户端发送一个事件通知来实现分布式的通知功能,然后客户端根据 Watcher 通知状态和事件类型做出业务上的改变。
工作机制:
- 客户端注册 watcher
- 服务端处理 watcher
- 客户端回调 watcher
Watcher 特性总结:
- 一次性
无论是服务端还是客户端,一旦一个 Watcher 被触发,Zookeeper 都会将其从相应的存储中移除。这样的设计有效的减轻了服务端的压力,不然对于更新非常频繁的节点,服务端会不断的向客户端发送事件通知,无论对于网络还是服务端的压力都非常大。 - 客户端串行执行
客户端 Watcher 回调的过程是一个串行同步的过程。 - 轻量
- Watcher 通知非常简单,只会告诉客户端发生了事件,而不会说明事件的具体内容。
- 客户端向服务端注册 Watcher 的时候,并不会把客户端真实的 Watcher 对象实体传递到服务端,仅仅是在客户端请求中使用 boolean 类型属性进行了标记。
- watcher event 异步发送 watcher 的通知事件从 server 发送到 client 是异步
的,这就存在一个问题,不同的客户端和服务器之间通过 socket 进行通信,由于网络延迟或其他因素导致客户端在不通的时刻监听到事件,由于 Zookeeper 本身提供了 ordering guarantee,即客户端监听事件后,才会感知它所监视 znode发生了变化。所以我们使用 Zookeeper 不能期望能够监控到节点每次的变化。Zookeeper 只能保证最终的一致性,而无法保证强一致性。 - 注册 watcher getData、exists、getChildren
- 触发 watcher create、delete、setData
- 当一个客户端连接到一个新的服务器上时,watch 将会被以任意会话事件触发。当与一个服务器失去连接的时候,是无法接收到 watch 的。而当 client 重新连接时,如果需要的话,所有先前注册过的 watch,都会被重新注册。通常这是完全透明的。只有在一个特殊情况下,watch 可能会丢失:对于一个未创建的 znode的 exist watch,如果在客户端断开连接期间被创建了,并且随后在客户端连接上之前又删除了,这种情况下,这个 watch 事件可能会被丢失。
5. 客户端注册 Watcher 实现?
- 调用 getData()/getChildren()/exist()三个 API,传入 Watcher 对象
- 标记请求 request,封装 Watcher 到 WatchRegistration
- 封装成 Packet 对象,发服务端发送 request
- 收到服务端响应后,将 Watcher 注册到 ZKWatcherManager 中进行管理
- 请求返回,完成注册。
6. 服务端处理 Watcher 实现
- 服务端接收 Watcher 并存储接收到客户端请求,处理请求判断是否需要注册Watcher,需要的话将数据节点的节点路径和 ServerCnxn(ServerCnxn 代表一个客户端和服务端的连接,实现了 Watcher 的 process 接口,此时可以看成一个 Watcher 对象)存储在WatcherManager 的 WatchTable 和 watch2Paths 中去。
- Watcher 触发
以服务端接收到 setData() 事务请求触发 NodeDataChanged 事件为例:- 封装 WatchedEvent将通知状态(SyncConnected)、事件类型(NodeDataChanged)以及节点路径封装成一个 WatchedEvent 对象
- 查询 Watcher从 WatchTable 中根据节点路径查找 Watcher
- 没找到;说明没有客户端在该数据节点上注册过 Watcher
- 找到;提取并从 WatchTable 和 Watch2Paths 中删除对应 Watcher(从这里可以看出 Watcher 在服务端是一次性的,触发一次就失效了)
- 调用 process 方法来触发 Watcher
这里 process 主要就是通过 ServerCnxn 对应的 TCP 连接发送 Watcher 事件通知。
7. 客户端回调Watcher
客户端 SendThread 线程接收事件通知,交由 EventThread 线程回调Watcher。
客户端的 Watcher 机制同样是一次性的,一旦被触发后,该 Watcher 就失效了。
其实这里的4,5,6,7都是在介绍watcher机制。从实现原理到实现步骤。这个所谓的watcher就是一个监听的功能。我们用日常生活中的定闹钟来理解:人主动在闹钟上定了一个时间(客户端在服务端定了一个触发事件)。到了时间闹钟会响起来(触发事件被触发服务端通知客户端)。人听到闹钟知道时间到了(客户端收到通知知道之前的事件被触发了)。单纯的理解功能是很简单的。不谈代码实现只从逻辑上来看也是很好理解的,甚至说这个一次性也好理解。毕竟普通的学生闹钟也都是一次性的。为了减少闹钟(服务端)的压力嘛。至于实现的细节百度上也很多教程贴。其实这一段我真的没啥实践经验,没有上一篇mybatis写的顺学的快。不过也在尽量多摄取知识点啦。有兴趣的自己去百度吧,这个watcher就到这。
8. ACL权限控制机制
ACL(Access Control List)访问控制列表
包括三个方面:
-
权限模式(Scheme)
- IP:从 IP 地址粒度进行权限控制
- Digest:最常用,用类似于 username:password 的权限标识来进行权限配置,便于区分不同应用来进行权限控制
- World:最开放的权限控制方式,是一种特殊的 digest 模式,只有一个权限标识“world:anyone”
- Super:超级用户
-
授权对象
授权对象指的是权限赋予的用户或一个指定实体,例如 IP 地址或是机器灯。 -
权限 Permission
- CREATE:数据节点创建权限,允许授权对象在该 Znode 下创建子节点
- DELETE:子节点删除权限,允许授权对象删除该数据节点的子节点
- READ:数据节点的读取权限,允许授权对象访问该数据节点并读取其数据内容或子节点列表等
- WRITE:数据节点更新权限,允许授权对象对该数据节点进行更新操作
- ADMIN:数据节点管理权限,允许授权对象对该数据节点进行 ACL 相关设置操作
说实话知识都是相互的,我看到这块莫名想起了shiro的权限框架,虽然完全是两个东西。但是我们知道正常一个超管admin账户拥有系统的最高全部权限。有点类似于这个super么?其次一个admin可以创建拥有不同权限的子账号。想给子账号分配什么功能就有什么功能,不想给分配的就没有。而这个分配功能的过程学名好像就叫授权。最后可以被拆分的,比如审核,看账单,做增删操作等是不同的权限,和上面介绍的create,delete,read,write,admin是不是有点那么类似?只不过是权限的划分是不一样的而已。
如实这么理解其实对于上面的概念还是比较容易理解的。但是具体的实现啥的肯定是不一样的,不过这个只是一个理论上的理解,具体区别我其实也不清楚,就这样吧。
9. 会话管理
分桶策略:将类似的会话放在同一区块中进行管理,以便于 Zookeeper 对会话进行不同区块的隔离处理以及同一区块的统一处理。
分配原则:每个会话的“下次超时时间点”(ExpirationTime)
计算公式:
ExpirationTime_ = currentTime + sessionTimeout
这里说的贼笼统,我是又看了别的介绍才勉强明白的。客户端与服务端之间任何交互操作都与会话息息相关,如临时节点的生命周期、客户端请求的顺序执行、Watcher通知机制等。Zookeeper的连接与会话就是客户端通过实例化Zookeeper对象来实现客户端与服务端创建并保持TCP连接的过程。
Session是Zookeeper中的会话实体,代表了一个客户端会话,其包含了如下四个属性:
- sessionID。会话ID,唯一标识一个会话,每次客户端创建新的会话时,Zookeeper都会为其分配一个全局唯一的sessionID。
- TimeOut。会话超时时间,客户端在构造Zookeeper实例时,会配置sessionTimeout参数用于指定会话的超时时间,Zookeeper客户端向服务端发送这个超时时间后,服务端会根据自己的超时时间限制最终确定会话的超时时间。
- TickTime。下次会话超时时间点,为了便于Zookeeper对会话实行"分桶策略"管理,同时为了高效低耗地实现会话的超时检查与清理,Zookeeper会为每个会话标记一个下次会话超时时间点,其值大致等于当前时间加上TimeOut。
- isClosing。标记一个会话是否已经被关闭,当服务端检测到会话已经超时失效时,会将该会话的isClosing标记为"已关闭",这样就能确保不再处理来自该会话的心情求了。
会话的分桶策略就是根据这个下次超时的时间点来分的。其实有点类似于心跳机制了,当超过某时间客户端还没反应则断开会话。然后被清理。这样可以简化操作而减耗。对了,当客户端每次发消息,这个下次超时时间都会更新。整体来讲可以理解为按照时间从近到远的顺序排会话。每次会话有客户端来的消息则会提到队列的最后。然后每次检查从头开始检查,如果保证顺序的话遇到第一个不超时的以后都不用查了。
但是注意我上面说的是排序,但是实际上人家不是一个个从小到大的排序,而是分桶。但是注意!!本质上桶排也是排序的一种哟~~反正意思就是这个意思。当然这个是我的理解,也不知道对不对,如果说的有问题欢迎指出。顺便附上一篇我觉得写会话这块很清楚的帖子zookeeper会话
10. 服务器角色
Leader(领导者)
- 事务请求的唯一调度和处理者,保证集群事务处理的顺序性
- 集群内部各服务的调度者
Follower(跟随者)
- 处理客户端的非事务请求,转发事务请求给 Leader 服务器
- 参与事务请求 Proposal 的投票
- 参与 Leader 选举投票
Observer(观察者)
- 3.0 版本以后引入的一个服务器角色,在不影响集群事务处理能力的基础上提升集群的非事务处理能力
- 处理客户端的非事务请求,转发事务请求给 Leader 服务器
- 不参与任何形式的投票
这个又没啥好说的,关于领导者,跟随者和观察者的关系就是这样。需要注意的是当有一半以上的人不跟领导者了这个领导者就会下台,新的谁获得一半以上的支持就上台。这个篡权的过程就叫崩溃恢复。
11. Zookeeper 下 Server 工作状态
服务器具有四种状态,分别是 LOOKING、FOLLOWING、LEADING、OBSERVING。
- LOOKING:寻找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。
- FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。
- LEADING:领导者状态。表明当前服务器角色是 Leader。
- OBSERVING:观察者状态。表明当前服务器角色是 Observer。
这个其实和上面的角色相对应。只不过多了个looking的造反状态而已。挺有意思的其实。
12. zookeeper 是如何保证事务的顺序一致性的?
zookeeper 采用了全局递增的事务 Id 来标识,所有的 proposal(提议)都在被提出的时候加上了 zxid,zxid 实际上是一个 64 位的数字,高 32 位是 epoch(时期; 纪元; 世; 新时代)用来标识 leader 周期,如果有新的 leader 产生出来,epoch会自增,低 32 位用来递增计数。当新产生 proposal 的时候,会依据数据库的两阶段过程,首先会向其他的 server 发出事务执行请求,如果超过半数的机器都能执行并且能够成功,那么就会开始执行。
感觉差不多就是每一个事务都有一个递增编号,根据这个编号来一个个处理,类似于移动大厅办业务要排队。因为一到了大厅就取号了,所以叫号的顺序和号是一样的,保证了顺序一致。
13. 数据同步
整个集群完成 Leader 选举之后,Learner(Follower 和 Observer 的统称)回向Leader 服务器进行注册。当 Learner 服务器想 Leader 服务器完成注册后,进入 数据同步环节。
数据同步流程:(均以消息传递的方式进行)
- Learner 向 Learder 注册
- 数据同步
- 同步确认
Zookeeper 的数据同步通常分为四类:
- 直接差异化同步(DIFF 同步)
- 先回滚再差异化同步(TRUNC+DIFF 同步)
- 仅回滚同步(TRUNC 同步)
- 全量同步(SNAP 同步)
在进行数据同步前,Leader 服务器会完成数据同步初始化:
peerLastZxid:从learner服务器注册时发送的ACKEPOCH消息中提取lastZxid(该 Learner服务器最后处理的ZXID)
minCommittedLog: Leader服务器Proposal缓存队列committedLog中最小ZXID
maxCommittedLog: Leader服务器Proposal缓存队列committedLog中最大ZXID
直接差异化同步(DIFF 同步):
场景:peerLastZxid介于minCommittedLog和maxCommittedLog 之间
先回滚再差异化同步(TRUNC+DIFF 同步):
场景:当新的Leader服务器发现某个Learner服务器包含了一条自己没 有的事务记录,那么就需要让该Learner服务器进行事务回滚--回滚到Leader 服务器上存在的,同时也是最接近于peerLastZxid的ZXID=
仅回滚同步(TRUNC 同步):
场景:peerLastZxid 大于 maxCommittedLog
全量同步(SNAP 同步):
场景一:peerLastZxid 小于 minCommittedLog
场景二:Leader 服务器上没有Proposal缓存队列且peerLastZxid不等 于lastProcessZxid
其实简单来说领导者有个待处理的队列。如果learner是在这队列中的某个,则直接同步。如果learner不是这个队列中的则换成里learner最近的那个同步。如果learner的比队列中最大值还大则回滚同步。如果说learner的小于最小值直接同步成领导者的那个。单文字表述的是这个意思。具体的因为没用过也没接触过所以只能单纯的文字理解。多少有个印象就行了。不过这四种同步办法还挺好记的,我是当作一个知识点来背的。
14. zookeeper是如何保证事务的顺序一致性的?
zookeeper 采用了全局递增的事务 Id 来标识,所有的 proposal(提议)都在被 提出的时候加上了 zxid,zxid 实际上是一个 64 位的数字,高 32 位是 epoch(时 期; 纪元; 世; 新时代)用来标识 leader周期,如果有新的 leader产生出来,epoch 会自增,低 32 位用来递增计数。当新产生 proposal 的时候,会依据数据库的两 阶段过程,首先会向其他的 server 发出事务执行请求,如果超过半数的机器都能 执行并且能够成功,那么就会开始执行。(简单来说就是递增编号来保证的,这个题我好像上面说过,不知道是不是记错了)
15. 分布式集群中为什么会有Master?
在分布式环境中,有些业务逻辑只需要集群中的某一台机器进行执行,其他的机 器可以共享这个结果,这样可以大大减少重复计算,提高性能,于是就需要进行 leader 选举.
16. zk节点宕机如何处理?
Zookeeper 本身也是集群,推荐配置不少于 3 个服务器。Zookeeper 自身也要保 证当一个节点宕机时,其他节点会继续提供服务。 如果是一个 Follower 宕机,还有 2 台服务器提供访问,因为 Zookeeper 上的数 据是有多个副本的,数据并不会丢失; 如果是一个 Leader 宕机,Zookeeper 会选举出新的 Leader。 ZK 集群的机制是只要超过半数的节点正常,集群就能正常提供服务。只有在 ZK 节点挂得太多,只剩一半或不到一半节点能工作,集群才失效。
- 所以 3 个节点的 cluster 可以挂掉 1 个节点(leader 可以得到 2 票>1.5)
- 2 个节点的 cluster 就不能挂掉任何 1 个节点了(leader 可以得到 1 票<=1)
17. zookeeper负载均衡和nginx负载均衡区别
zk 的负载均衡是可以调控,nginx 只是能调权重,其他需要可控的都需要自己写插件;但是 nginx 的吞吐量比 zk 大很多,应该说按业务选择用哪种方式
18. Zookeeper有哪几种几种部署模式?
部署模式:单机模式(我现在正在用的)、伪集群模式、集群模式。
19. 集群最少要几台机器,集群规则是怎样的?
集群规则为 2N+1 台,N>0,即 3 台。
关于这个为什么最少三台其实上面的宕机处理就可以说明。
20. 集群支持动态添加机器吗?
其实就是水平扩容了,Zookeeper 在这方面不太好。两种方式:
全部重启:关闭所有 Zookeeper 服务,修改配置之后启动。不影响之前客户端的 会话。
逐个重启:在过半存活即可用的原则下,一台机器重启不影响整个集群对外提供 服务。这是比较常用的方式。
21. Zookeeper对节点的watch监听通知是永久的吗?为什么不是永久的?
其实这个答案挺多的,但是我觉得精简几句话:不是永久的,是一次性的。这样做的目的是减压减耗。
22. 说几个zookeeper常用的命令。
常用命令:ls get set create delete 等
23. ZAB和Paxos算法的联系与区别?
相同点:
- 两者都存在一个类似于 Leader 进程的角色,由其负责协调多个 Follower 进程 的运行
- Leader 进程都会等待超过半数的 Follower 做出正确的反馈后,才会将一个提 案进行提交
- ZAB 协议中,每个 Proposal 中都包含一个 epoch 值来代表当前的 Leader 周期,Paxos 中名字为 Ballot
不同点:
ZAB 用来构建高可用的分布式数据主备系统(Zookeeper),Paxos 是用来构建 分布式一致性状态机系统。
最后还有一道开放性的大题比较复杂,而且不是一句两句说得清的。所以我这里也就不记录了。关于本书中zookeeper的问答题大概就是如上这些。对我个人而言确实挺难的,我说过我zookeeper只用过单体用于注册中心的。 这篇文章整理起来真的有点烦,差不多一道题百度一下,看好几个技术贴,理解了再用自己的话表述一遍。反正整体来说多看书涨知识吧。我一个朋友也说不是要每个题都背下来,有个印象有个理解就行了。
这篇笔记就到这里,如果稍微帮到你了记得点个喜欢点个关注,也祝大家工作顺顺利利!!另外java技术交流群130031711,群里好多学习资料啥的,欢迎各位萌新大佬踊跃加入!