没错!本文就是zookeeper入门的不二之选
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务。
是一个为分布式应用提供一致性服务的软件,可以提供配置维护、域名服务、分布式同步、组服务等等功能。是一个使用非常广泛的开源软件。
本文对zookeeper做一些整体上的介绍,让初学者能够尽快了解zk的全貌。
1·ZK的整体架构
1.1·集群——机器节点
1.1.1·客户端连接server节点读操作
读请求由客户端所连接到的server直接处理返回,但是因为iserver节点之间数据同步会有网络延迟等问题,不同客户端读到的数据可能不一样
zookeeper保证各节点最终的数据一致,如果需要最新数据,可以在读数据之前调用sync()接口
注解:zookeeper的sync
sync是使得client当前连接着的ZooKeeper服务器,和ZooKeeper的Leader节点同步(sync)一下数据。
当follower收到到sync请求时,会将这个请求添加到一个pendingSyncs队列里,然后将这个请求发送给leader,直到收到leader的Leader.SYNC消息时,才将这个请求从pendingSyncs队列里移除,并commit这个请求。
当Leader收到一个sync请求时,如果leader当前没有待commit的决议,那么leader会立即发送一个Leader.SYNC消息给follower。否则,leader会等到当前最后一个待commit的决议完成后,再发送Leader.SYNC消息给Follower。
如果leader和follower之间的消息通信,是严格按顺序来发送的(TCP保证),因此,当follower接收到Leader.SYNC消息时,说明follower也一定接收到了leader之前(在leader接收到sync请求之前)发送的所有提案或者commit消息。这样,就可以确保follower和leader是同步的了。
1.1.2·客户端连接server节点写操作
1.2·ZNode——数据节点
2·watcher——对ZNode进行监控
ZK允许客户端向服务端注册一个Watcher监听,当服务点的的指定事件触发监听时,那么服务端就会向客户端发送事件通知,以便客户端完成逻辑操作
(即客户端向服务端注册监听,并将watcher对象存在客户端的Watchermanager中,服务端触发事件后,向客户端发送通知,客户端收到通知后从wacherManager中取出对象来执行回调逻辑)
2.1·watcher整体流程图
2.2·可设置watcher的函数
getData、getChildren、exist接口
2.3·可触发watcher的事件
事件 | 说明 |
NodeCreated (1) | Watcher监听的数据节点被创建时 |
NodeDeleted (2) | Watcher监听的数据节点被删除时 |
NodeDataChanged (3) | Watcher监听的数据节点内容发生变更时(无论内容数据是否变化) |
NodeChildrenChanged (4) | Watcher监听的数据节点的子节点列表发生变更时 |
2.4·watcher的特性&注意点
1·Watcher是一次性的,一旦被触发就会移除,再次使用时需要重新注册
2·Watcher回调是顺序串行化执行的,只有回调后客户端才能看到最新的数据状态。一个Watcher回调逻辑不应该太多,以免影响别的watcher执行
3·WatchEvent是最小的通信单元,结构上只包含通知状态、事件类型和节点路径,并不会告诉数据节点变化前后的具体内容
4·Watcher只有在当前session彻底失效时才会无效,若在session有效期内快速重连成功,则watcher依然存在,仍可接收到通知
3·ZAB
ZooKeeper Atomic Broadcast (ZAB, ZooKeeper原子消息广播协议)是ZooKeeper实现分布式数据一致性的核心算法,ZAB借鉴Paxos算法,但又不像Paxos算法(一种基于消息传递的一致性算法)那样,是一种通用的分布式一致性算法,它是一种特别为ZooKeeper专门设计的支持崩溃恢复的原子广播协议。
根据ZAB协议,所有的写操作都必须通过leader来完成,leader写入本地日志后再复制到所有的follower节点。如果客户端对follower/observer发起写请求,follower/observer会将请求转发到leader,然后由leader处理完成后再将结果转发回follower/observer发送给客户端。
ZAB协议分为消息广播和崩溃恢复模式
3.1·协议过程
3.2·消息广播
1. 客户端发起一个写操作请求
2. Leader服务器将客户端的request请求转化为事物proposql提案,同时为每个proposal分配一个全局唯一的ID,即ZXID。
3. leader服务器与每个follower之间都有一个队列,leader将消息发送到该队列
4. follower机器从队列中取出消息处理完(写入本地事物日志中)毕后,向leader服务器发送ACK确认。
5. leader服务器收到半数以上的follower的ACK后,即认为可以发送commit
6. leader向所有的follower服务器发送commit消息。
关键词注解:
1.ZXID:·在消息广播中,leader会将每一个事务请求生成proposal,并在对其进行广播之前给这个事务proposal生成一个全局唯一的递增的 事务id,这个事务id就是ZXID,zookeeper会按照ZXID的先后顺序对事务进行处理,保证了事务的顺序执行。
·ZXID是一个64位的数字:
【低32位】可以看成一个简单的单增计数器,针对客户端每一个事务请求,Leader 在产生新的 Proposal 事务时,都会对该计数器加1。
【高32位】则代表了 Leader 周期的 epoch 编号。
(epoch 编号可以理解为当前集群所处的年代,或者周期。每次Leader变更之后都会在 epoch 的基础上加1,这样旧的 Leader 崩溃恢复之后,其他Follower 也不会听它的了,因为 Follower 只服从epoch最高的 Leader 命令)
·每当选举产生一个新的 Leader ,就会从这个 Leader 服务器上取出本地事务日志中最大编号 Proposal 的 zxid,并从 zxid 中解析得到对应的 epoch 编号,然后再对其加1,之后该编号就作为新的 epoch 值,并将低32位数字归零,由0开始重新生成zxid。
2.FIFO消息队列:每个follower都有一个FIFO的消息队列,用于leader和follower之间收发消息,使用队列,leader与follower只需要往队列中发送消息即可,实现异步,避免了同步可能造成的阻塞,队列是FIFO的,保证了事务的顺序执行。
3.3·崩溃恢复
在集群启动和集群运行期间leader异常的时候,会进入崩溃恢复模式进行leader的选举
3.3.1·相关名词解释
这些关键词在日志中也可以看到,
·WorkerReceiver:接收投票信息的工作线程
·WorkerSender:发送投票信息的工作线程
·QuorumPeer:这个类就是zookeeper的Leader选举的启动类,负责创建选举算法,zk数据恢复,启动leader选举等
·QuorumCnxManager(qcm):实现领导选举中的网络连接管理功能。它为每一对节点维护唯一的一个连接,在两个节点都启动申请连接时,只有sid大的一方才会申请连接成功。qcm对每个节点维护一个消息发送队列。
·NIOServerCnxn:处理与客户端之间的通信,使用单线程处理
·LearnerHandler:
为了保证整个集群内部的实时通信,同时为了确保可以控制所有的Follower/Observer服务器,Leader服务器会与每个Follower/Observer服务器建立一个TCP长连接。同时也会为每个Follower/Observer服务器创建一个名为LearnerHandler的实体。
LearnerHandler是Learner服务器的管理者,主要负责Follower/Observer服务器和Leader服务器之间的一系列网络通信,包括数据同步、请求转发和Proposal提议的投票等。
Leader服务器中保存了所有Follower/Observer对应的LearnerHandler。
Leader和每个Learner连接会维持一个长连接,并有一个单独的LearnerHandler线程和一个Learner进行交互
·peerEpoch:每次leader选举完成之后,都会选举出一个新的peerEpoch,用来标记事务请求所属的轮次
·electionEpoch:每执行一次leader选举,electionEpoch就会自增,用来标记leader选举的轮次
·peerLastZxid:Learner服务器(Follower或observer)最后处理的zxid。
·lastProcessedZxid:最后一次commit的事务请求的zxid(leader服务器最大的ZXID)
·minCommittedLog:Leader服务器proposal缓存队列committedLog中的最小的zxid。
·maxCommittedLog:Leader服务器proposal缓存队列committedLog中的最大的zxid。
3.3.2·成为leader的条件
1)选 epoch 最大的
2)若 epoch 相等,选 zxid 最大的
3)若 epoch 和 zxid 相等,选择 server_id 最大的(zoo.cfg中的myid)
Zab协议需要保证选举出来的Leader需要满足以下条件:
1)新选举出来的 Leader 不能包含未提交的 Proposal 。
即新选举的 Leader 必须都是已经提交了 Proposal 的 Follower 服务器节点。
2)新选举的 Leader 节点中含有最大的 zxid 。
这样做的好处是可以避免 Leader 服务器检查 Proposal 的提交和丢弃工作。
3.3.3·选举过程中节点的服务器状态
在leader选举过程中,服务器有下面四种状态:
·LOOKING:寻找leader状态,只要当前服务器状态为LOOKING,进入循环,不断地读取其它Server发来的通知、进行比较、更新自己的投票、发送自己的投票、统计投票结果,直到leader选出或出错退出。
·LEADING:领导状态(节点为leader)
·FOLLOWING:跟随者状态
·OBSERVING:观察者状态(此状态不参与选举)
3.3.4·集群启动时的选举过程
因为选举需要至少两台机器,所以当启动两个服务器的时候,选举就开始了,换句话说,集群启动的时候,leader会在最开始启动的两个服务器中选举出来
这里以server-1(myid=1),server-2(myid=2),server-3(myid=3),三个服务器节点的集群为例:
【1】选举示意图
【2】集群启动的日志解读
【第一步】先启动server-1
日志报错连接不上另外两个节点,目前集群是不可用的,SERVER-1一开始进入LOOKING状态
【第二步】再启动server-2
目前有了两个节点,则开始leader选举,因为ZXID相同,所以选举了myid较大的server-2
——新起的server-2节点日志:
——server-1节点日志:
【第三步】再启动server-3
因为集群已经有了leader,所以server-3作为了follower加入了集群
server-2节点(leader)的日志:
server-3节点(新follower)的日志:
3.3.5·集群运行期间的选举过程
集群运行期间因为leader节点崩溃会导致leader的选举,整体和集群启动时的leader选举差不多,示意图如下
3.3.6·leader选举之后的数据同步
数据同步方式有四种,决定用哪一种,是根据下面的四个参数
·peerLastZxid:Learner服务器(Follower或observer)最后处理的zxid。
·lastProcessedZxid:最后一次commit的事务请求的zxid(leader服务器最大的ZXID)
·minCommittedLog:Leader服务器proposal缓存队列committedLog中的最小的zxid。
·maxCommittedLog:Leader服务器proposal缓存队列committedLog中的最大的zxid。
【1】同步方式1:DIFF(直接差异化同步)
当Follower最大的zxid小于maxCommittedZxid且大于minCommittedZxid
【2】同步方式2:TRUNC+DIFF(先回滚再差异化同步)
当新的 Leader 服务器发现某个 Learner 服务器包含了一条自己没有的事务记录,那么就需要让该 Learner 服务器进行事务回滚--回滚到 Leader服务器上存在的,同时也是最接近于 peerLastZxid 的 ZXID
【3】同步方式3:TRUNC(仅回滚同步)
当Follower最大的zxid大于maxCommittedZxid时,该方式要求Follower丢弃超出的那部分Proposal
【4】同步方式4:SNAP(全量同步)
当Follower最大的zxid小于minCommittedZxid时,该方式直接同步快照给Follower
或者
Leader 服务器上没有 Proposal 缓存队列且 peerLastZxid 不等 于 lastProcessZxid