关于java:图解源码Zookeeper37源码分析包含服务启动流程源码网络通信源码RequestProcessor处理请求源码

7次阅读

共计 12806 个字符,预计需要花费 33 分钟才能阅读完成。

Zookeeper3.7 源码分析

能力指标

  • 能基于 Maven 导入最新版 Zookeeper 源码
  • 能说出 Zookeeper 单机启动流程
  • 了解 Zookeeper 默认通信中 4 个线程的作用
  • 把握 Zookeeper 业务解决源码解决流程
  • 可能在 Zookeeper 源码中 Debug 测试通信过程

1 Zookeeper 源码导入

Zookeeper 是一个高可用的分布式数据管理和协调框架,并且可能很好的保障分布式环境中数据的一致性。在越来越多的分布式系。在越来越多的分布式系统(Hadoop、HBase、Kafka)中,Zookeeper 都作为外围组件应用。

咱们以后课程次要是钻研 Zookeeper 源码,须要将 Zookeeper 工程导入到 IDEA 中,老版的 zk 是通过 ant 进行编译的,但最新的 zk(3.7)源码中曾经没了build.xml,而多了pom.xml,也就是说构建形式由原先的 Ant 变成了 Maven,源码下下来后,间接编译、运行是跑不起来的,有一些配置须要调整。

1.1 工程导入

Zookeeper 各个版本源码下载地址 https://github.com/apache/zoo…,咱们能够在该仓库下抉择不同的版本,咱们抉择最新版本,以后最新版本为 3.7,如下图:

找到我的项目下载地址,咱们抉择 https 地址,并复制该地址,通过该地址把我的项目导入到 IDEA 中。

点击 IDEA 的VSC>Checkout from Version Controller>GitHub,操作如下图:

克隆我的项目到本地:

我的项目导入本地后,成果如下:

我的项目运行的时候,缺一个版本对象,创立org.apache.zookeeper.version.Info,代码如下:

public interface Info {
    public static final int MAJOR=3;
    public static final int MINOR=4;
    public static final int MICRO=6;
    public static final String QUALIFIER=null;
    public static final int REVISION=-1;
    public static final String REVISION_HASH = "1";
    public static final String BUILD_DATE="2020-12-03 09:29:06";
}

1.2 Zookeeper 源码谬误解决

zookeeper-server 中找到 org.apache.zookeeper.server.quorum.QuorumPeerMain 并启动该类,启动前做如下配置:

启动的时候会会报很多谬误,比方缺包、缺对象,如下几幅图:

为了解决下面的谬误,咱们须要手动引入一些包,pom.xml引入如下依赖:

<!-- 引入依赖 -->
<dependency>
  <groupId>io.dropwizard.metrics</groupId>
  <artifactId>metrics-core</artifactId>
  <version>3.1.0</version>
</dependency>
<dependency>
  <groupId>org.xerial.snappy</groupId>
  <artifactId>snappy-java</artifactId>
  <version>1.1.7.3</version>
</dependency>
<dependency>
  <groupId>org.eclipse.jetty</groupId>
  <artifactId>jetty-server</artifactId>
</dependency>
<dependency>
  <groupId>org.eclipse.jetty</groupId>
  <artifactId>jetty-servlet</artifactId>
</dependency>
<dependency>
  <groupId>commons-cli</groupId>
  <artifactId>commons-cli</artifactId>
</dependency>

1.3 Zookeeper 命令(自学)

咱们要想学习 Zookeeper,须要先学会应用 Zookeeper,它有很多丰盛的命令,借助这些命令能够深刻了解 Zookeeper,咱们启动源码中的客户端就能够应用 Zookeeper 相干命令。

启动客户端org.apache.zookeeper.ZooKeeperMain,如下图:

启动后,日志如下:

1)节点列表:ls /

ls /
[dubbo, zookeeper]
ls /dubbo
[com.itheima.service.CarService]

2)查看节点状态:stat /dubbo

stat /dubbo
cZxid = 0x3
ctime = Thu Dec 03 09:19:29 CST 2020
mZxid = 0x3
mtime = Thu Dec 03 09:19:29 CST 2020
pZxid = 0x4
cversion = 1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 13
numChildren = 1

节点信息参数阐明如下:

key value
cZxid = 0x3 创立节点时的事务 ID
ctime = Thu Dec 03 09:19:29 CST 2020 最初批改节点时的事务 ID
mZxid = 0x31 最初批改节点时的事务 ID
mtime = Sat Mar 16 15:38:34 CST 2019 最初批改节点时的工夫
pZxid = 0x31 示意该节点的子节点列表最初一次批改的事务 ID,增加子节点或删除子节点就会影响子节点列表,然而批改子节点的数据内容则不影响该 ID(留神,只有子节点列表变更了才会变更 pzxid,子节点内容变更不会影响 pzxid)
cversion = 0 子节点版本号,子节点每次批改版本号加 1
dataVersion = 0 数据版本号,数据每次批改该版本号加 1
aclVersion = 0 权限版本号,权限每次批改该版本号加 1
ephemeralOwner = 0x0 创立该长期节点的会话的 sessionID。(如果该节点是长久节点,那么这个属性值为 0)
dataLength = 22 该节点的数据长度
numChildren = 0 该节点领有子节点的数量(只统计间接子节点的数量)

3)创立节点:create /dubbo/code java

create /dubbo/code java
Created /dubbo/code

其中 code 示意节点,java 示意节点下的内容。

4)查看节点数据:get /dubbo/code

get /dubbo/code
java

5)删除节点:delete /dubbo/code || deleteall /dubbo/code

删除没有子节点的节点:delete /dubbo/code

删除所有子节点:deleteall /dubbo/code

6)历史操作命令:history

history
1 - ls /dubbo
2 - ls /dubbo/code
3 - get /dubbo/code
4 - get /dubbo/code
5 - create /dubbo/code java
6 - get /dubbo/code
7 - get /dubbo/code
8 - delete /dubbo/code
9 - get /dubbo/code
10 - listquota path
11 - history

1.4 Zookeeper 剖析工具

Zookeeper 装置比拟不便,在装置一个集群当前,查看数据却比拟麻烦,上面介绍 Zookeeper 的数据查看工具——ZooInspector。

下载地址:https://issues.apache.org/jir…

下载压缩包后,解压后,咱们须要运行zookeeper-dev-ZooInspector.jar

输出账号密码,就能够连贯 Zookeeper 了,如下图:

连贯后,Zookeeper 信息如下:

节点操作:减少节点、批改节点、删除节点

1.5 Zookeeper 案例利用

咱们将材料中 工程 \dubbo工程导入到 IDEA 中,上图是他们的调用关系,那么问题来了:

  • 生产者向 Zookeeper 注册服务信息,Zookeeper 把数据存哪儿了?
  • 集群环境下,如果某个节点数据变更了,Zookeeper 如何监听到的?
  • 集群环境下各个节点的数据如何同步?
  • 如果某个节点挂了,Zookeeper 如何选举呢?
  • ……..

带着下面的疑难,咱们开始钻研 Zookeeper 源码。

2 ZK 服务启动流程源码分析

ZooKeeper能够以 standalone、分布式的形式部署,standalone 模式下只有一台机器作为服务器,ZooKeeper会丢失高可用个性,分布式是应用多个机器, 每台机器上部署一个 ZooKeeper 服务器, 即便有服务器宕机, 只有少于半数,ZooKeeper集群仍然能够失常对外提供服务,集群状态下 Zookeeper 是具备高可用个性。

咱们接下来对 ZooKeeperstandalone模式启动以及集群模式做一下源码剖析。

2.1 ZK 单机 / 集群启动流程

如上图,上图是 Zookeeper 单机 / 集群启动流程,每个细节所做的事件都在上图有阐明,咱们接下来依照流程图对源码进行剖析。

2.2 ZK 启动入口剖析

启动入口类:QuorumPeerMain

该类是 zookeeper 单机 / 集群的启动入口类,是用来加载配置、启动 QuorumPeer(选举相干) 线程、创立 ServerCnxnFactory 等,咱们能够把代码切换到该类的主办法 (main) 中,从该类的主办法开始剖析,main办法代码剖析如下:

下面 main 办法尽管只是做了初始化配置,但调用了 initializeAndRun() 办法,initializeAndRun()办法中会依据配置来决定启动单机 Zookeeper 还是集群 Zookeeper,源码如下:

如果启动单机版,会调用ZooKeeperServerMain.main(args);,如果启动集群版,会调用QuorumPeerMain.runFromConfig(config);,咱们接下来对单机版启动做源码具体分析,集群版在前面章节中解说选举机制时具体解说。

2.3 ZK 单机启动源码分析

针对 ZK 单机启动源码办法调用链,咱们曾经提前做了一个办法调用关系图,咱们解说 ZK 单机启动源码,将和该图进行一一匹对,如下图:

1)单机启动入口

依照下面的源码剖析,咱们找到 ZooKeeperServerMain.main(args) 办法,该办法调用了 ZooKeeperServerMaininitializeAndRun办法,在 initializeAndRun 办法中执行初始化操作,并运行 Zookeeper 服务,main 办法如下:

2)配置文件解析

initializeAndRun()办法会注册 JMX,同时解析 zoo.cfg 配置文件,并调用 runFromConfig() 办法启动 Zookeeper 服务,源码如下:

3)单机启动主流程

runFromConfig办法是单机版启动的次要办法,该办法会做如下几件事:

1: 初始化各类运行指标,比方一次提交数据最大破费多长时间、批量同步数据大小等。2: 初始化权限操作,例如 IP 权限、Digest 权限。3: 创立事务日志操作对象,Zookeeper 中每次减少节点、批改数据、删除数据都是一次事务操作,都会记录日志。4: 定义 Jvm 监控变量和常量,例如正告工夫、告警阀值次数、提醒阀值次数等。5: 创立 ZookeeperServer,这里只是创立,并不在 ZooKeeperServerMain 类中启动。6: 启动 Zookeeper 的控制台治理对象 AdminServer,该对象采纳 Jetty 启动。7: 创立 ServerCnxnFactory,该对象其实是 Zookeeper 网络通信对象,默认应用了 NIOServerCnxnFactory。8: 在 ServerCnxnFactory 中启动 ZookeeperServer 服务。9: 创立并启动 ContainerManager,该对象通过 Timer 定时执行, 清理过期的容器节点和 TTL 节点, 执行周期为分钟。10: 避免主线程完结,阻塞主线程。

办法源码如下:

4)网络通信对象创立

下面办法在创立网络通信对象的时候调用了 ServerCnxnFactory.createFactory(), 该办法其实是依据系统配置创立 Zookeeper 通信组件,可选的有NIOServerCnxnFactory(默认)NettyServerCnxnFactory,对于通信对象咱们会在前面进行具体解说,该办法源码如下:

5)单机启动

cnxnFactory.startup(zkServer);办法其实就是启动了 ZookeeperServer,它调用NIOServerCnxnFactorystartup办法,该办法中会调用 ZookeeperServerstartup办法启动服务,ZooKeeperServerMain运行到 shutdownLatch.await(); 主线程会阻塞住, 源码如下:

启动后,日志如下:

3 ZK 网络通信源码分析

Zookeeper作为一个服务器, 天然要与客户端进行网络通信, 如何高效的与客户端进行通信, 让网络 IO 不成为 ZooKeeper 的瓶颈是 ZooKeeper 急需解决的问题,ZooKeeper中应用 ServerCnxnFactory 治理与客户端的连贯, 其有两个实现, 一个是 NIOServerCnxnFactory, 应用 Java 原生NIO 实现; 一个是 NettyServerCnxnFactory, 应用 netty 实现; 应用ServerCnxn 代表一个客户端与服务端的连贯。

从单机版启动中能够发现 Zookeeper 默认通信组件为 NIOServerCnxnFactory,他们和ServerCnxnFactory 的关系如下图:

3.1 NIOServerCnxnFactory 工作流程

个别应用 Java NIO 的思路为应用 1 个线程组监听 OP_ACCEPT 事件, 负责解决客户端的连贯; 应用 1 个线程组监听客户端连贯的 OP_READOP_WRITE事件, 解决 IO 事件 (netty 也是这种实现形式).
但 ZooKeeper 并不是如此划分线程性能的,NIOServerCnxnFactory启动时会启动四类线程:

1:accept thread: 该线程接管来自客户端的连贯, 并将其调配给 selector thread(启动一个线程)。2:selector thread: 该线程执行 select(), 因为在解决大量连贯时,select()会成为性能瓶颈, 因而启动多个 selector thread, 应用零碎属性 zookeeper.nio.numSelectorThreads 配置该类线程数, 默认个数为 外围数 /2。3:worker thread: 该线程执行根本的套接字读写, 应用零碎属性 zookeeper.nio.numWorkerThreads 配置该类线程数, 默认为外围数∗2 外围数∗2. 如果该类线程数为 0, 则另外启动一线程进行 IO 解决, 见下文 worker thread 介绍。4:connection expiration thread: 若连贯上的 session 已过期, 则敞开该连贯。

这四个线程在 NIOServerCnxnFactory 类上有阐明,如下图:

ZooKeeper中对线程须要解决的工作做了更细的拆分, 解决了有大量客户端连贯的状况下,selector.select()会成为性能瓶颈, 将 selector.select() 拆分进去, 交由 selector thread 解决。

3.2 NIOServerCnxnFactory 源码

NIOServerCnxnFactory 的源码剖析咱们将依照下面所介绍的 4 个线程实现相干剖析,并实现数据操作,在程序中获取指定数据。

3.2.1 AcceptThread 分析

为了让大家更容易了解 AcceptThread,咱们把它的构造和办法调用关系画了一个具体的流程图,如下图:

NIOServerCnxnFactory 类中有一个 AccpetThread 线程,为什么说它是一个线程?咱们看下它的继承关系:AcceptThread > AbstractSelectThread > ZooKeeperThread > Thread,该线程接管来自客户端的连贯, 并将其调配给 selector thread(启动一个线程)。

该线程执行流程:run执行 selector.select(), 并调用doAccept() 接管客户端连贯,因而咱们能够着重关注 doAccept() 办法, 该类源码如下:

doAccept()办法用于解决客户端链接,当客户端链接 Zookeeper 的时候,首先会调用该办法,调用该办法执行过程如下:

1: 和以后服务建设链接。2: 获取近程客户端计算机地址信息。3: 判断以后链接是否超出最大限度。4: 调整为非阻塞模式。5: 轮询获取一个 SelectorThread,将以后链接调配给该 SelectorThread。6: 将以后申请增加到该 SelectorThread 的 acceptedQueue 中,并唤醒该 SelectorThread。

doAccept()办法源码如下:

下面代码中 addAcceptedConnection 办法如下:

咱们把我的项目中的分布式案例服务启动,能够看到如下日志打印:

AcceptThread---------- 链接服务的 IP:127.0.0.1
3.2.2 SelectorThread 分析

同样为了更容易梳理SelectorThread,咱们也把它的构造和办法调用关系梳理成了流程图,如下图:

该线程的次要作用是从 Socket 读取数据,并封装成 workRequest,并将workRequest 交给 workerPool 工作线程池解决,同时将 acceptedQueue 中未解决的链接取出,并未每个链接绑定 OP_READ 读事件,并封装对应的上下文对象 NIOServerCnxnSelectorThread 的 run 办法如下:

run()办法中会调用 select(),而select() 中的外围调用中央是 handleIO(),咱们看名字其实就晓得这里是解决客户端申请的数据,但客户端申请数据并非在SelectorThread 线程中解决,咱们接着看 handleIO() 办法。

handleIO()办法会封装以后 SelectorThreadIOWorkRequest, 并将 IOWorkRequest 交给 workerPool 来调度,而 workerPool 调度才是读数据的开始,源码如下:

3.2.3 WorkerThread 分析

WorkerThread 相比下面的线程而言,调用关系颇为简单,设计到了多个对象办法调用,次要用于解决 IO,但并未对数据做出解决,数据处理将有业务链对象 RequestProcessor 解决,调用关系图如下:

ZooKeeper中通过 WorkerService 治理一组 worker thread 线程, 后面咱们在看 SelectorThread 的时候,可能看到 workerPool 的 schedule 办法被执行,如下图:

咱们跟踪 workerPool.schedule(workRequest); 能够发现它调用了WorkerService.schedule(workRequest) > WorkerService.schedule(WorkRequest, long),该办法创立了一个新的线程ScheduledWorkRequest, 并启动了该线程,源码如下:

ScheduledWorkRequest实现了 Runnable 接口,并在 run() 办法中调用了 IOWorkRequest 中的 doWork 办法,在该办法中会调用 doIO 执行 IO 数据处理,源码如下:

IOWorkRequestdoWork 源码如下:

接下来的调用链路比较复杂,咱们把外围步骤列出,在能间接看到数据读取的中央详细分析源码。下面办法调用链路:NIOServerCnxn.doIO()>readPayload()>readRequest() >ZookeeperServer.processPacket() ,最初一步办法是获取外围数据的中央,咱们能够批改下代码读取数据:

增加测试代码如下:

//========== 测试 Start===========
// 定义接管输出流对象(输入流)ByteArrayOutputStream os = new ByteArrayOutputStream();

// 将网络输出流读取到输入流中
byte[] buffer = new byte[1024];
int len=0;
while ((len=bais.read(buffer))!=-1){os.write(buffer,0,len);
}
String result = new String(os.toByteArray(),"UTF-8");
System.out.println("processPacket--------------- 读到的数据:"+result);
//========== 测试 End===========

咱们启动客户端创立一个 demo 节点,并增加数据为 abcdefg

create /demo abcdefg

控制台数据如下:

测试实现后,不要忘了将该测试正文掉。咱们能够执行其余增删改查操作,能够输入 RequestHeader.type 查看操作类型,操作类型代码在 ZooDefs 中有标识, 罕用的操作类型如下:

int create = 1;
int delete = 2;
int exists = 3;
int getData = 4;
int setData = 5;
int getACL = 6;
int setACL = 7;
int getChildren = 8;
int sync = 9;
int ping = 11;
2.3.4 ConnectionExpirerThread 分析

后盾启动 ConnectionExpirerThread 清理线程清理过期的session, 线程中有限循环, 执行工作如下:

2.3 ZK 通信优劣总结

Zookeeper 在通信方面默认应用了 NIO,并反对扩大 Netty 实现网络数据传输。相比传统 IO,NIO 在网络数据传输方面有很多显著劣势:

1: 传统 IO 在解决数据传输申请时,针对每个传输申请生成一个线程,如果 IO 异样,那么线程阻塞,在 IO 复原后唤醒解决线程。在同时解决大量连贯时,会实例化大量的线程对象。每个线程的实例化和回收都须要耗费资源,jvm 须要为其调配 TLAB,而后初始化 TLAB,最初绑定线程,线程完结时又须要回收 TLAB,这些都须要 CPU 资源。2:NIO 应用 selector 来轮询 IO 流,外部应用 poll 或者 epoll,以事件驱动模式来相应 IO 事件的解决。同一时间只需实例化很少的线程对象,通过对线程的复用来进步 CPU 资源的应用效率。3:CPU 轮流为每个线程调配工夫片的模式,间接的实现单物理核解决多线程。当线程越多时,每个线程调配到的工夫片越短,或者循环调配的周期越长,CPU 很多工夫都消耗在了线程的切换上。线程切换蕴含线程上个线程数据的同步(TLAB 同步),同步变量同步至主存,下个线程数据的加载等等,他们都是很消耗 CPU 资源的。4: 在同时解决大量连贯,但沉闷连贯不多时,NIO 的事件响应模式相比于传统 IO 有着极大的性能晋升。NIO 还提供了 FileChannel,以 zero-copy 的模式传输数据,相较于传统的 IO,数据不须要拷贝至用户空间,可间接由物理硬件 (磁盘等) 通过内核缓冲区后间接传递至网关,极大的进步了性能。5:NIO 提供了 MappedByteBuffer,其将文件间接映射到内存(这里的内存指的是虚拟内存,并不是物理内存),能极大的进步 IO 吞吐能力。

ZK 在应用 NIO 通信尽管大幅晋升了数据传输能力,但也存在一些代码诟病问题:

1:Zookeeper 通信源码局部学习老本高,须要把握 NIO 和多线程
2: 多线程应用频率高,耗费资源多,但性能失去晋升
3:Zookeeper 数据处理调用链路简单,多处存在外部类,代码构造不清晰,写法比拟经典

4 RequestProcessor 解决申请源码分析

zookeeper 的业务解决流程就像工作流一样,其实就是一个单链表;在 zookeeper 启动的时候,会确立各个节点的角色个性,即 leaderfollowerobserver,每个角色确立后,就会初始化它的工作责任链;

4.1 RequestProcessor 构造

客户端申请过去,每次执行不同事务操作的时候,Zookeeper 也提供了一套业务解决流程 RequestProcessorRequestProcessor 的解决流程如下图:

咱们来看一下 RequestProcessor 初始化流程,ZooKeeperServer.setupRequestProcessors()办法源码如下:

它的创立步骤:

1: 创立 finalProcessor。2: 创立 syncProcessor,并将 finalProcessor 作为它的下一个业务链。3: 启动 syncProcessor。4: 创立 firstProcessor(PrepRequestProcessor),将 syncProcessor 作为 firstProcessor 的下一个业务链。5: 启动 firstProcessor。

syncProcessor创立时,将 finalProcessor 作为参数传递进来源码如下:

firstProcessor创立时,将 syncProcessor 作为参数传递进来源码如下:

PrepRequestProcessor/SyncRequestProcessor关系图:

PrepRequestProcessorSyncRequestProcessor 的构造一样,都是实现了 Thread 的一个线程,所以在这里初始化时便启动了这两个线程。

4.2 PrepRequestProcessor 分析

PrepRequestProcessor是申请处理器的第 1 个处理器,咱们把之前的申请业务解决衔接起来,一步一步剖析。ZooKeeperServer.processPacket()>submitRequest()>enqueueRequest()>RequestThrottler.submitRequest() ,咱们来看下 RequestThrottler.submitRequest() 源码, 它将以后申请增加到 submittedRequests 队列中了,源码如下:

RequestThrottler 继承了 ZooKeeperCriticalThread > ZooKeeperThread > Thread,也就是说以后 RequestThrottler 是个线程,咱们看看它的 run 办法做了什么事,源码如下:

RequestThrottler调用了 ZooKeeperServer.submitRequestNow() 办法,而该办法又调用了 firstProcessor 的办法,源码如下:

ZooKeeperServer.submitRequestNow()办法调用了 firstProcessor.processRequest() 办法,而这里的 firstProcessor 就是初始化业务解决链中的 PrepRequestProcessor,也就是说三个RequestProecessor 中最先调用的是PrepRequestProcessor

PrepRequestProcessor.processRequest()办法将以后申请增加到了队列 submittedRequests 中,源码如下:

下面办法中并未从 submittedRequests 队列中获取申请,如何执行申请的呢,因为 PrepRequestProcessor 是一个线程,因而会在 run 中执行,咱们查看 run 办法源码的时候发现它调用了 pRequest() 办法,pRequest()办法源码如下:

首先先执行 pRequestHelper() 办法,该办法是 PrepRequestProcessor 解决外围业务流程,次要是一些过滤操作,操作实现后,会将申请交给下一个业务链,也就是 SyncRequestProcessor.processRequest() 办法解决申请。

咱们来看一下 PrepRequestProcessor.pRequestHelper() 办法做了哪些事,源码如下:

从下面源码能够看出 PrepRequestProcessor.pRequestHelper() 办法判断了客户端操作类型,但无论哪种操作类型简直都调用了 pRequest2Txn() 办法,咱们来看看源码:

从下面代码能够看出 pRequest2Txn() 办法次要做了权限校验、快照记录、事务信息记录相干的事,还并未波及数据处理,也就是说 PrepRequestProcessor 其实是做了操作前权限校验、快照记录、事务信息记录相干的事。

咱们 DEBUG 调试一次,看看业务解决流程是否和咱们下面所剖析的统一。

增加节点:

create /zkdemo itheima

DEBUG 测试如下:

客户端申请先通过 ZooKeeperServer.submitRequestNow() 办法,并调用 firstProcessor.processRequest() 办法,而firstProcessor=PrepRequestProcessor,如下图:

进入 PrepRequestProcessor.pRequest() 办法,执行完 pRequestHelper() 办法后,开始执行下一个业务链的办法,而下一个业务链nextProcessor=SyncRequestProcessor,如下测试图:

4.3 SyncRequestProcessor 分析

剖析了 PrepRequestProcessor 处理器后,接着来剖析SyncRequestProcessor,该处理器次要是将申请数据高效率存入磁盘,并且申请在写入磁盘之前是不会被转发到下个处理器的。

咱们先看申请被增加到队列的办法:

同样 SyncRequestProcessor 是一个线程,执行队列中的申请也在线程中触发,咱们看它的 run 办法,源码如下:

run办法会从 queuedRequests 队列中获取一个申请,如果获取不到就会阻塞期待直到获取到一个申请对象,程序才会持续往下执行,接下来会调用 Snapshot Thread 线程实现将客户端发送的数据以快照的形式写入磁盘,最终调用 flush() 办法实现数据提交,flush()办法源码如下:

flush()办法实现了数据提交,并且会将申请交给下一个业务链,下一个业务链为FinalRequestProcessor

4.4 FinalRequestProcessor 分析

后面剖析了SyncReqeustProcessor,接着剖析申请解决链中最初的一个处理器FinalRequestProcessor, 该业务解决对象次要用于返回 Response。

4.5 ZK 业务链解决优劣总结

Zookeeper 业务链解决,思维遵循了 AOP 思维,但并未采纳相干技术,为了晋升效率,依然大幅应用到了多线程。正因为有了业务链路解决先后顺序,使得 Zookeeper 业务解决流程更清晰更容易了解,但大量混入了多线程,也似的学习成本增加。

本文由传智教育博学谷 – 狂野架构师教研团队公布,转载请注明出处!

如果本文对您有帮忙,欢送关注和点赞;如果您有任何倡议也可留言评论或私信,您的反对是我保持创作的能源

正文完
 0