Zookeeper
2024-10-30 19:30:08 15 举报
AI智能生成
Zookeeper是一个开源的分布式协调服务,由Apache Software Foundation开发。它主要用于分布式系统中的数据发布/订阅、负载均衡、命名服务、分布式锁等功能。Zookeeper的核心内容是提供高性能、高可用的协调服务,支持多种编程语言和框架。它以文件系统的方式存储数据,通过Watcher机制实现数据的发布/订阅。此外,Zookeeper还提供了ACL(访问控制列表)来控制数据的访问权限。
作者其他创作
大纲/内容
前置
分布式系统定义及⾯临的问题
分布式系统的协调⼯作就是通过某种⽅式,让每个节点的信息能够同步和共享。这依赖于服务进程之间的通信
① 通过⽹络进⾏信息共享
② 通过共享存储
这就好⽐开发leader按照约定的时间和路径,把任务分配表放到了svn上,组员每天去svn上拉取最新的 任务分配表,然后⼲活。其中svn就是共享存储。更好⼀点的做法是,当svn⽂件版本更新时,触发邮件 通知,每个组员再去拉取最新的任务分配表。这样做更好,因为每次更新,组员都能第⼀时间得到消 息,从⽽让⾃⼰⼿中的任务分配表永远是最新的。此种⽅式依赖于中央存储
ZooKeeper如何解决分布式系统⾯临的问题
ZooKeeper对分布式系统的协调,使⽤的是第⼆种⽅式,共享存储。其实共享存储,分布式应⽤也需要和存储进⾏⽹络通信
注:Slave节点要想获取ZooKeeper的更新通知,需事先在关⼼的数据节点上设置观察点(设置监听事件)
⼤多数分布式系统中出现的问题,都源于信息的共享出了问题。如果各个节点间信息不能及时共享和同步,那么就会在协作过程中产⽣各种问题。ZooKeeper解决协同问题的关键,就是在于保证分布式系统信息的⼀致性
介绍
是什么?
Zookeeper 是一个分布式的、开源的分布式应用程序的协调服务。是Apache Hadoop 项目下的一个子项目,是一个树形目录服务
基础概念
集群角色
leader
follower
observer
数据节点(Znode)
会话(Session)
Session指客户端会话,⼀个客户端连接是指客户端和服务端之间的⼀个TCP⻓连接,从第⼀次连接建⽴开 始,客户端会话的⽣命周期也开始了,通过这个连接,客户端能够⼼跳检测与服务器保持有效的会话, 也能够向Zookeeper服务器发送请求并接受响应,同时还能够通过该连接接受来自服务器的Watch事件通知。
版本(Version)
Zookeeper的每个Znode上都会存储数据,对于每个ZNode,Zookeeper都会为其维护 ⼀个叫作Stat的数据结构,Stat记录了这个ZNode的三个数据版本,分别是`version(当前ZNode的版 本)、cversion(当前ZNode⼦节点的版本)、aversion(当前ZNode的ACL版本)`
Watcher事件监听
Wathcer(事件监听器),是Zookeeper中⼀个很重要的特性,Zookeeper允许⽤户在指定节点上注册 ⼀些Watcher,并且在⼀些特定事件触发的时候,Zookeeper服务端会将事件通知到感兴趣的客户端, 该机制是Zookeeper实现分布式协调服务的重要特性
ACL (Acess Control List) 权限配置
CREATE:创建⼦节点的权限
READ:获取节点数据和⼦节点列表的权限。
WRITE:更新节点数据的权限
DELETE:删除⼦节点的权限。
ADMIN:设置节点ACL的权限。
CREATE和DELETE这两种权限都是针对⼦节点的权限控制
主要功能
配置管理
把项目中所用到的配置文件进行抽取,所有的项目从配置管理中心获取(统一管理的目的:为了提高程序的维护性),如果把程序的配置信息保存在zk的 znode`节点下,当修改配置时,即 Znode`会发生变化,可以通过改变ZK 中某个目录节点的内容,利用Watcher 机制(监听机制)通知给各个客户端,从而更改配置
集群管理
集群管理包括集群监控和集群控制,就是监控集群机器状态,剔除机器和加入机器。zookeeper可以方便集群机器的管理,它可以实时监控znode节点的变化,一旦发现有机器挂了,该机器就会与 ZK 断开连接,对应的临时目录节点会被删除,其他所有机器都收到通知。新机器加入也是类似(添加一个创建节点的监听)
命名服务(JNDI)
通过指定的名字来获取资源或者服务地址。`Zookeeper可以创建一个全局唯一的`路径`,这个路径就可以作为一个名字。被命名的实体可以是集群中的机器,服务的地址,或者是远程的对象等。一些分布式服务框架(RPC、RMI)中的服务地址列表,通过使用命名服务,客户端应用能够根据特定的名字来获取资源的实体、服务地址和提供者信息等(`可以联想下 Dubbo 借助Zookeeper 存储的信息`)
分布式锁
临时顺序节点 + Watcher
ZNode节点信息
整个 ZNode 节点内容包括两部分:节点数据内容和节点状态信息
Watcher 变更通知
Zookeeper使⽤Watcher机制实现分布式数据的 发布/订阅 功能
Zookeeper的Watcher机制主要包括客户端线程、客户端WatcherManager、Zookeeper服务器三部 分。
具体⼯作流程为:客户端在向Zookeeper服务器注册的同时,会将Watcher对象存储在客户端的 `WatcherManager`当中。当Zookeeper服务器触发Watcher事件后,会向客户端发送通知,客户端线程 从WatcherManager中取出对应的Watcher对象来执⾏回调逻辑
具体⼯作流程为:客户端在向Zookeeper服务器注册的同时,会将Watcher对象存储在客户端的 `WatcherManager`当中。当Zookeeper服务器触发Watcher事件后,会向客户端发送通知,客户端线程 从WatcherManager中取出对应的Watcher对象来执⾏回调逻辑
通过客服端和服务端分别创建有观察者的信息列表。客户端调用 getData、exist 等接口时,首先将对应的 Watch 事件放到本地的 ZKWatchManager 中进行管理。服务端在接收到客户端的请求后根据请求类型判断是否含有 Watch 事件,并将对应事件放到 WatchManager 中进行管理。
在事件触发的时候服务端通过节点的路径信息查询相应的 Watch 事件通知给客户端,客户端在接收到通知后,首先查询本地的 ZKWatchManager 获得对应的 Watch 信息处理回调操作。这种设计不但实现了一个分布式环境下的观察者模式,而且通过将客户端和服务端各自处理 Watch 事件所需要的额外信息分别保存在两端,减少彼此通信的内容。大大提升了服务的处理性能。
在事件触发的时候服务端通过节点的路径信息查询相应的 Watch 事件通知给客户端,客户端在接收到通知后,首先查询本地的 ZKWatchManager 获得对应的 Watch 信息处理回调操作。这种设计不但实现了一个分布式环境下的观察者模式,而且通过将客户端和服务端各自处理 Watch 事件所需要的额外信息分别保存在两端,减少彼此通信的内容。大大提升了服务的处理性能。
ACL 保证数据安全
授权模式(Scheme)
IP
Digest
Digest是最常⽤的权限控制模式,要更符合我们对权限控制的认识,其使 ⽤"username:password"形式的权限标识来进⾏权限配置,便于区分不同应⽤来进⾏权限控制。 当我们通过“username:password”形式配置了权限标识后,Zookeeper会先后对其进⾏`SHA-1加密 和BASE64编码`。
world
World是⼀种最开放的权限控制模式,这种权限控制⽅式⼏乎没有任何作⽤,数据节点的访问权限 对所有⽤户开放,即所有⽤户都可以在不进⾏任何权限校验的情况下操作ZooKeeper上的数据
super
Super模式,顾名思义就是超级⽤户的意思,也是⼀种特殊的Digest模式。在Super模式下,超级⽤户可以对任意ZooKeeper上的数据节点进⾏任何操作
授权对象(ID)
IP
Digest
super
权限
CREATE(C)
DELETE(D)
READ(R)
WRITE(W)
ADMIN(A)
数据模型
命令操作
服务端命令
客户端命令
程序操作
原生API ZooKeeper
zkClient
Curator
分布式锁
为什么需要分布式锁?
在我们进行单机应用开发,涉及并发同步的时候,我们往往采用 synchronized 或者Lock 的方式来解决多线程间的代码同步问题,这时多线程的运行都是在同一个JVM之下,没有任何问题。(jdk中内置的锁:synchronized 或者 lock 锁都是 JVM 级别的,在分布式环境下每一个服务都存在独立的 jvm, 在分布式环境下 synchronized 或者 lock 都会失效)
实现思路
通过一个公共的组件来存储锁(就是一条数据,通过这条数据用来标识记录中是否存在锁)
分布式锁应该具备的条件
1、在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行;【安全】
2、高可用的获取锁与释放锁;【效率】
3、高性能的获取锁与释放锁;
4、具备可重入[reentrant]特性;
5、具备锁失效机制,防止死锁; 【安全、效率】
6、具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。
2、高可用的获取锁与释放锁;【效率】
3、高性能的获取锁与释放锁;
4、具备可重入[reentrant]特性;
5、具备锁失效机制,防止死锁; 【安全、效率】
6、具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。
分布式锁的三种实现方式
对比
Zookeeper分布式锁实现原理
公平锁
竞争锁
公平锁主要依据的是zookeeper的Sequence+Ephemeral节点(顺序临时)的特性实现的
在线程启动时会根据线程进行编号,由于Sequence节点的特性,每个线程均能成功创建出节点,此处节点的选举有些类似于zookeeper的选举,在启动时会根据节点的编号顺序来指定主节点,例如有三个节点,编号分别为1,2,3,此时会指定最小的节点为leader,其余的节点为follower,同时此节点对应的线程watch是比自己节点小的节点,也就是说3线程watch2节点,2线程watch1节点
在线程启动时会根据线程进行编号,由于Sequence节点的特性,每个线程均能成功创建出节点,此处节点的选举有些类似于zookeeper的选举,在启动时会根据节点的编号顺序来指定主节点,例如有三个节点,编号分别为1,2,3,此时会指定最小的节点为leader,其余的节点为follower,同时此节点对应的线程watch是比自己节点小的节点,也就是说3线程watch2节点,2线程watch1节点
释放锁
Leader 如果主动放弃领导权,直接删除其创建的节点即可.
如果 Leader 所在进程意外宕机,其与 Zookeeper 间的 Session 结束,由于其创建的节点为Ephemeral类型,故该节点自动被删除.
如果 Leader 所在进程意外宕机,其与 Zookeeper 间的 Session 结束,由于其创建的节点为Ephemeral类型,故该节点自动被删除.
感知锁的释放
与非公平模式不同,每个 Follower 并非都 Watch 由 Leader 创建出来的节点,而是 Watch 序号刚好比自己序号小的节点,所以主节点释放后刚好比主节点序号大的节点就会感知到,比如:1节点释放后2线程会watch到1节点释放锁从而竞争锁,但是在竞争锁之前会判断此节点是否是最小的节点,如果不是仍然不会成为主节点.(1节点释放锁之前2节点宕机的情况下,3线程会watch到2节点的释放,此时3线程会判断3节点是否是最小的节点,由于此时1节点没有删除,所以3节点不会成为leader,并且3线程会watch比2节点小的节点也就是1节点)
实现相对复杂
扩展性好,每个客户端都只 Watch 一个节点且每次节点被删除只须通知一个客户端
旧 Leader 放弃领导权时,其它客户端根据竞选的先后顺序(也即节点序号)成为新 Leader,这也是公平模式的由来
延迟相对非公平模式要高,因为它必须等待特定节点得到通知才能选出新的 Leader
扩展性好,每个客户端都只 Watch 一个节点且每次节点被删除只须通知一个客户端
旧 Leader 放弃领导权时,其它客户端根据竞选的先后顺序(也即节点序号)成为新 Leader,这也是公平模式的由来
延迟相对非公平模式要高,因为它必须等待特定节点得到通知才能选出新的 Leader
非公平锁
竞争锁
非公平模式的zookeeper的分布式锁使用的是Non-sequence+Ephemeral节点(临时,非顺序)实现的,此节点的实现方式和Redis实现分布式锁的实现方式比较类似.
zookeeper由于Non-sequence节点的特性,在创建节点时,多个节点只会创建一个成功,这个节点就是主节点,其余的节点就是follower,这样就保证了只有一个线程能够拿到锁
释放锁
由于Ephemeral节点的存在,锁的获得者应该能够正确释放已经获得的锁,并且当获得锁的进程宕机时,锁应该自动释放,从而使得其它竞争方可以获得该锁,从而避免出现死锁的状态
或者leader主动释放锁,并且当领导所在进程宕机时,领导权应该自动释放,从而使得其它参与者可重新竞争领导而避免进入无主状态
或者leader主动释放锁,并且当领导所在进程宕机时,领导权应该自动释放,从而使得其它参与者可重新竞争领导而避免进入无主状态
感知锁的释放
感知锁的释放主要是watch机制的存在,在leader释放锁时,节点删除,其他线程会感知到锁的释放,从而竞争锁
非公平模式实现简单,每一轮选举方法都完全一样
竞争参与方不多的情况下,效率高。每个 Follower 通过 Watch 感知到节点被删除的时间不完全一样,只要有一个 Follower 得到通知即发起竞选,即可保证当时有新的 Leader 被选出
给Zookeeper 集群造成的负载大,因此扩展性差。如果有上万个客户端都参与竞选,意味着同时会有上万个写请求发送给 Zookeper。如《Zookeeper架构》一文所述,Zookeeper 存在单点写的问题,写性能不高。同时一旦 Leader 放弃领导权,Zookeeper 需要同时通知上万个 Follower,负载较大。
竞争参与方不多的情况下,效率高。每个 Follower 通过 Watch 感知到节点被删除的时间不完全一样,只要有一个 Follower 得到通知即发起竞选,即可保证当时有新的 Leader 被选出
给Zookeeper 集群造成的负载大,因此扩展性差。如果有上万个客户端都参与竞选,意味着同时会有上万个写请求发送给 Zookeper。如《Zookeeper架构》一文所述,Zookeeper 存在单点写的问题,写性能不高。同时一旦 Leader 放弃领导权,Zookeeper 需要同时通知上万个 Follower,负载较大。
应用场景
数据订阅/发布
数据发布/订阅(Publish/Subscribe)系统,即所谓的配置中⼼
发布/订阅系统⼀般有两种设计模式,分别是推(Push)模式和拉(Pull)模式。在推模式中,服务端 主动将数据更新发送给所有订阅的客户端;⽽拉模式则是由客户端主动发起请求来获取最新数据,通常 客户端都采⽤定时进⾏轮询拉取的⽅式。
ZooKeeper 采⽤的是推拉相结合的⽅式:客户端向服务端注册⾃⼰需要关注的节点,⼀旦该节点的数据 发⽣变更,那么服务端就会向相应的客户端发送Watcher事件通知,客户端接收到这个消息通知之后, 需要主动到服务端获取最新的数据。
ZooKeeper 采⽤的是推拉相结合的⽅式:客户端向服务端注册⾃⼰需要关注的节点,⼀旦该节点的数据 发⽣变更,那么服务端就会向相应的客户端发送Watcher事件通知,客户端接收到这个消息通知之后, 需要主动到服务端获取最新的数据。
如果将配置信息存放到ZooKeeper上进⾏集中管理,那么通常情况下,应⽤在启动的时候都会主动到 ZooKeeper服务端上进⾏⼀次配置信息的获取,同时,在指定节点上注册⼀个Watcher监听,这样⼀ 来,但凡配置信息发⽣变更,服务端都会实时通知到所有订阅的客户端,从⽽达到实时获取最新配置信息的⽬的。
命名服务(JNID)
命名服务(Name Service)也是分布式系统中⽐较常⻅的⼀类场景,是分布式系统最基本的公共服务之⼀。在分布式系统中,被命名的实体通常可以是集群中的机器、提供的服务地址或远程对象等——这些我们都可以统称它们为名字(Name),其中较为常⻅的就是⼀些分布式服务框架(如RPC、RMI)中 的服务地址列表,通过使⽤命名服务,客户端应⽤能够根据指定名字来获取资源的实体、服务地址和提 供者的信息等。
集群管理
随着分布式系统规模的⽇益扩⼤,集群中的机器规模也随之变⼤,那如何更好地进⾏集群管理也显得越来越重要了。所谓集群管理,包括集群监控与集群控制两⼤块,前者侧重对集群运⾏时状态的收集,后者则是对集群进⾏操作与控制
集群环境下所要面对的问题
- 如何快速的统计出当前⽣产环境下⼀共有多少台机器
- 如何快速的获取到机器上下线的情况
- 如何实时监控集群中每台主机的运⾏时状态
在传统的基于Agent的分布式集群管理体系中,都是通过在集群中的每台机器上部署⼀个 Agent,由这 个 Agent 负责主动向指定的⼀个监控中⼼系统(监控中⼼系统负责将所有数据进⾏集中处理,形成⼀系列报表,并负责实时报警,以下简称“监控中⼼”)汇报⾃⼰所在机器的状态。在集群规模适中的场景下,这确实是⼀种在⽣产实践中⼴泛使⽤的解决⽅案,能够快速有效地实现分布式环境集群监控,但是 ⼀旦系统的业务场景增多,集群规模变⼤之后,该解决⽅案的弊端也就显现出来了。
⼤规模升级困难
以客户端形式存在的 Agent,在⼤规模使⽤后,⼀旦遇上需要⼤规模升级的情况,就⾮常麻烦
统⼀的Agent⽆法满⾜多样的需求
对于机器的CPU使⽤率、负载(Load)、内存使⽤率、⽹络吞吐以及磁盘容量等机器基本的物理状态, 使⽤统⼀的Agent来进⾏监控或许都可以满⾜。但是,如果需要深⼊应⽤内部,对⼀些业务状态进⾏监控,例如,在⼀个分布式消息中间件中,希望监控到每个消费者对消息的消费状态;或者在⼀个分布式任务调度系统中,需要对每个机器上任务的执⾏情况进⾏监控。很显然,对于这些业务耦合紧密的监控 需求,不适合由⼀个统⼀的Agent来提供
编程语⾔多样性
Zookeeper的两⼤特性
1、客户端如果对Zookeeper的数据节点注册Watcher监听,那么当该数据节点的内容或是其⼦节点 列表发⽣变更时,Zookeeper服务器就会向订阅的客户端发送变更通知。
2、对在Zookeeper上创建的临时节点,⼀旦客户端与服务器之间的会话失效,那么临时节点也会被⾃动删除
2、对在Zookeeper上创建的临时节点,⼀旦客户端与服务器之间的会话失效,那么临时节点也会被⾃动删除
Master选举
分布式锁
排它锁
共享锁
分布式队列
分布式队列可以简单分为两⼤类:⼀种是常规的FIFO先⼊先出队列模型,还有⼀种是 等待队列元素聚 集后统⼀安排处理执⾏的Barrier模型
FIFO先⼊先出
使⽤ZooKeeper实现FIFO队列,和之前提到的共享锁的实现⾮常类似。FIFO队列就类似于⼀个全写的共享锁模型,⼤体的设计思路其实⾮常简单:所有客户端都会到/queue_fifo 这个节点下⾯创建⼀个临时 顺序节点,例如如/queue_fifo/host1-00000001
创建完节点后,根据如下4个步骤来确定执⾏顺序。
1. 通过调⽤getChildren接⼝来获取/queue_fifo节点的所有⼦节点,即获取队列中所有的元素。
2. 确定⾃⼰的节点序号在所有⼦节点中的顺序。
3. 如果⾃⼰的序号不是最⼩,那么需要等待,同时向⽐⾃⼰序号⼩的最后⼀个节点注册Watcher监 听。
4. 接收到Watcher通知后,重复步骤1
1. 通过调⽤getChildren接⼝来获取/queue_fifo节点的所有⼦节点,即获取队列中所有的元素。
2. 确定⾃⼰的节点序号在所有⼦节点中的顺序。
3. 如果⾃⼰的序号不是最⼩,那么需要等待,同时向⽐⾃⼰序号⼩的最后⼀个节点注册Watcher监 听。
4. 接收到Watcher通知后,重复步骤1
Barrier:分布式屏障
Barrier原意是指障碍物、屏障,⽽在分布式系统中,特指系统之间的⼀个协调条件,规定了⼀个队列的 元素必须都集聚后才能统⼀进⾏安排,否则⼀直等待。这往往出现在那些⼤规模分布式并⾏计算的应⽤ 场景上:最终的合并计算需要基于很多并⾏计算的⼦结果来进⾏。
这些队列其实是在 FIFO 队列的基础 上进⾏了增强,⼤致的设计思想如下:开始时,/queue_barrier 节点是⼀个已经存在的默认节点,并且 将其节点的数据内容赋值为⼀个数字n来代表Barrier值,例如n=10表示只有当/queue_barrier节点下的 ⼦节点个数达到10后,才会打开Barrier。之后,所有的客户端都会到/queue_barrie节点下创建⼀个临 时节点,例如/queue_barrier/host1,如图所示。
这些队列其实是在 FIFO 队列的基础 上进⾏了增强,⼤致的设计思想如下:开始时,/queue_barrier 节点是⼀个已经存在的默认节点,并且 将其节点的数据内容赋值为⼀个数字n来代表Barrier值,例如n=10表示只有当/queue_barrier节点下的 ⼦节点个数达到10后,才会打开Barrier。之后,所有的客户端都会到/queue_barrie节点下创建⼀个临 时节点,例如/queue_barrier/host1,如图所示。
创建完节点后,按照如下步骤执⾏。
1. 通过调⽤getData接⼝获取/queue_barrier节点的数据内容:10。
2. 通过调⽤getChildren接⼝获取/queue_barrier节点下的所有⼦节点,同时注册对⼦节点变更的 Watcher监听。
3. 统计⼦节点的个数。
4. 如果⼦节点个数还不⾜10个,那么需要等待。
5. 接受到Wacher通知后,重复步骤2
1. 通过调⽤getData接⼝获取/queue_barrier节点的数据内容:10。
2. 通过调⽤getChildren接⼝获取/queue_barrier节点下的所有⼦节点,同时注册对⼦节点变更的 Watcher监听。
3. 统计⼦节点的个数。
4. 如果⼦节点个数还不⾜10个,那么需要等待。
5. 接受到Wacher通知后,重复步骤2
集群
集群角色
Leader 领导者
Follower 跟随者
Observer 观察者
怎么同步数据的?
搭建几台实例?
ZAB协议
定义
ZAB【Zookeeper Atomic Broadcast(Zookeeper 原子广播协议)】 协议是为分布式协调服务 Zookeeper 专门设计的一种支持 崩溃恢复 和 原子广播 协议
整个 Zookeeper 就是在【消息广播 和 崩溃恢复】之间切换。当 Leader 服务可以正常使用,就进入消息广播模式,当 Leader 不可用时,则进入崩溃恢复模式
ZAB核心
简化版本的2PC
整个消息⼴播协议是基于具有FIFO特性的TCP协议来进⾏⽹络通信的
所有事务请求必须由⼀个全局唯⼀的服务器来协调处理,这样的服务器被称为Leader服务器,余下的服务器则称为Follower服务器,Leader服务器负责将⼀个客户端事务请求转化成⼀个事务Proposal(提议),并将该Proposal分发给集群中所有的Follower服务器,之后Leader服务器需要等待所有Follower服务器的反馈ACK(以事务⽇志的形式写⼊到本地磁盘中去,并且在成功写⼊后反馈给Leader服务器⼀个Ack响应),⼀旦超过半数的Follower服务器进⾏了正确的反馈后,那么Leader就会再次向所有的Follower服务器分发Commit消息,要求其将前⼀个Proposal进⾏提交。
运⾏时状态分析
- LOOKING:Leader选举阶段。
- FOLLOWING:Follower服务器和Leader服务器保持同步状态。
- LEADING:Leader服务器作为主进程领导状态。
所有进程初始状态都是LOOKING状态,此时不存在Leader,接下来,进程会试图选举出⼀个新的 Leader,之后,如果进程发现已经选举出新的Leader了,那么它就会切换到FOLLOWING状态,并开始和Leader保持同步,处于FOLLOWING状态的进程称为Follower,LEADING状态的进程称为Leader,当Leader崩溃或放弃领导地位时,其余的Follower进程就会转换到LOOKING状态开始新⼀轮的Leader选举
服务器⻆⾊
Leader
Leader服务器是Zookeeper集群⼯作的核⼼,其主要⼯作有以下两个:
- 事务请求的唯⼀调度和处理者,保证集群事务处理的顺序性
- 集群内部各服务器的调度者
请求处理链
可以看到,从prepRequestProcessor到FinalRequestProcessor前后⼀共7个请求处理器组成了leader 服务器的请求处理链
(1) PrepRequestProcessor。请求预处理器 ,也是leader服务器中的第⼀个请求处理器。在Zookeeper 中,那些会改变服务器状态的请求称为事务请求(创建节点、更新数据、删除节点、创建会话等), PrepRequestProcessor能够识别出当前客户端请求是否是事务请求。对于事务请求, PrepRequestProcessor处理器会对其进⾏⼀系列预处理,如创建请求事务头、事务体、会话检查、ACL 检查和版本检查等。
(2) ProposalRequestProcessor。事务投票处理器。也是Leader服务器事务处理流程的发起者,对 于⾮事务性请求,ProposalRequestProcessor会直接将请求转发到CommitProcessor处理器,不再做 任何处理,⽽对于事务性请求,处理将请求转发到CommitProcessor外,还会根据请求类型创建对应的 Proposal提议,并发送给所有的Follower服务器来发起⼀次集群内的事务投票。同时, ProposalRequestProcessor还会将事务请求交付给SyncRequestProcessor进⾏事务⽇志的记录。
(3) SyncRequestProcessor。事务⽇志记录处理器。⽤来将事务请求记录到事务⽇志⽂件中,同时 会触发Zookeeper进⾏数据快照。
(4) AckRequestProcessor。负责在SyncRequestProcessor完成事务⽇志记录后,向Proposal的投 票收集器发送ACK反馈,以通知投票收集器当前服务器已经完成了对该Proposal的事务⽇志记录。
(5) CommitProcessor。事务提交处理器。对于⾮事务请求,该处理器会直接将其交付给下⼀级处理器处理;对于事务请求,其会等待集群内 针对Proposal的投票直到该Proposal可被提交,利⽤ `CommitProcessor`,每个服务器都可以很好地控制对事务请求的顺序处理。
(6) ToBeCommitProcessor。该处理器有⼀个toBeApplied队列,⽤来存储那些已经被 `CommitProcessor`处理过的可被提交的Proposal。其会将这些请求交付给FinalRequestProcessor处理 器处理,待其处理完后,再将其从toBeApplied队列中移除。
(7) FinalRequestProcessor ⽤来进⾏客户端请求返回之前的操作,包括创建客户端请求的响应, 针对事务请求,该处理器还会负责将事务应⽤到内存数据库中。
(1) PrepRequestProcessor。请求预处理器 ,也是leader服务器中的第⼀个请求处理器。在Zookeeper 中,那些会改变服务器状态的请求称为事务请求(创建节点、更新数据、删除节点、创建会话等), PrepRequestProcessor能够识别出当前客户端请求是否是事务请求。对于事务请求, PrepRequestProcessor处理器会对其进⾏⼀系列预处理,如创建请求事务头、事务体、会话检查、ACL 检查和版本检查等。
(2) ProposalRequestProcessor。事务投票处理器。也是Leader服务器事务处理流程的发起者,对 于⾮事务性请求,ProposalRequestProcessor会直接将请求转发到CommitProcessor处理器,不再做 任何处理,⽽对于事务性请求,处理将请求转发到CommitProcessor外,还会根据请求类型创建对应的 Proposal提议,并发送给所有的Follower服务器来发起⼀次集群内的事务投票。同时, ProposalRequestProcessor还会将事务请求交付给SyncRequestProcessor进⾏事务⽇志的记录。
(3) SyncRequestProcessor。事务⽇志记录处理器。⽤来将事务请求记录到事务⽇志⽂件中,同时 会触发Zookeeper进⾏数据快照。
(4) AckRequestProcessor。负责在SyncRequestProcessor完成事务⽇志记录后,向Proposal的投 票收集器发送ACK反馈,以通知投票收集器当前服务器已经完成了对该Proposal的事务⽇志记录。
(5) CommitProcessor。事务提交处理器。对于⾮事务请求,该处理器会直接将其交付给下⼀级处理器处理;对于事务请求,其会等待集群内 针对Proposal的投票直到该Proposal可被提交,利⽤ `CommitProcessor`,每个服务器都可以很好地控制对事务请求的顺序处理。
(6) ToBeCommitProcessor。该处理器有⼀个toBeApplied队列,⽤来存储那些已经被 `CommitProcessor`处理过的可被提交的Proposal。其会将这些请求交付给FinalRequestProcessor处理 器处理,待其处理完后,再将其从toBeApplied队列中移除。
(7) FinalRequestProcessor ⽤来进⾏客户端请求返回之前的操作,包括创建客户端请求的响应, 针对事务请求,该处理器还会负责将事务应⽤到内存数据库中。
Follower
Follower服务器是Zookeeper集群状态中的跟随者,其主要⼯作有以下三个:
- 处理客户端⾮事务性请求(读取数据),转发事务请求给Leader服务器。
- 参与事务请求Proposal的投票。
- 参与Leader选举投票。
和leader⼀样,Follower也采⽤了责任链模式组装的请求处理链来处理每⼀个客户端请求,由于不需要对事务请求的投票处理,因此Follower的请求处理链会相对简单,其处理链如下
(1) FollowerRequestProcessor:其⽤作识别当前请求是否是事务请求,若是,那么Follower就会将该请求转发给Leader服务器, Leader服务器在接收到这个事务请求后,就会将其提交到请求处理链,按照正常事务请求进⾏处理。
(2) SendAckRequestProcessor: 其承担了事务⽇志记录反馈的⻆⾊,在完成事务⽇志记录后,会向Leader服务器发送ACK消息以表明⾃身完成了事务⽇志的记录⼯作
(2) SendAckRequestProcessor: 其承担了事务⽇志记录反馈的⻆⾊,在完成事务⽇志记录后,会向Leader服务器发送ACK消息以表明⾃身完成了事务⽇志的记录⼯作
Observer
Observer是ZooKeeper⾃3.3.0版本开始引⼊的⼀个全新的服务器⻆⾊。从字⾯意思看,该服务器充当了⼀个观察者的⻆⾊——其观察ZooKeeper集群的最新状态变化并将这些状态变更同步过来。 Observer服务器在⼯作原理上和Follower基本是⼀致的,对于⾮事务请求,都可以进⾏独⽴的处理,⽽ 对于事务请求,则会转发给Leader服务器进⾏处理。和Follower唯⼀的区别在于,Observer不参与任何形式的投票,包括事务请求Proposal的投票和Leader选举投票。简单地讲,Observer服务器只提供 ⾮事务服务,通常⽤于在不影响集群事务处理能⼒的前提下提升集群的⾮事务处理能⼒。
另外需要注意的⼀点是,虽然在图中可以看到,Observer 服务器在初始化阶段会将 SyncRequestProcessor处理器也组装上去,但是在实际运⾏过程中,Leader服务器不会将事务请求的 投票发送给Observer服务器
原子(消息)广播
崩坏恢复
数据同步
0 条评论
下一页