因为在Zookeeper的底层源码中大量使用了NIO,线程和阻塞队列,在了解之前对前面这些有个基础会更容易理解

ZAB 是Zookeeper 的一种原子广播协议,用于支持Zookeeper 的分布式协调一致性和奔溃恢复的一种,但是ZAB 协议的源码比上一篇Zookeeper的Leader选举算法要复杂一些,所以分多篇进行分析

首先来一张简单的架构图,对Zookeeper客户端架构有个基本概念

这里从客户端开始,我们来看一下zk 的一个简单使用,下面是创建zk客户端,然后创建一个节点

new ZookeeperZookeeper

zookeeper.createzookeeper.create

Zookeeper客户端初始化

Zookeeper



createConnection 实际上是创建了一个ClientCnxn 对象,在这个对象中保存了一些数据,比如苏红zookeeper自身引用,监视器,sessionId,过期时间,配置信息, 这里我们需要重点关注的是创建了两个线程sendThreadeventThread,这两个线程一个是用于发送数据,一个是用于事件处理的,详见ClientCnxn

zk 客户端启动,详见cnxn.start

ClientCnxn

ClientCnxn

ClientCnxn#start

cnxn.start



这里很简单,就是直接启动了初始化创建的两个线程,sendThread,eventThread, 因为这是两个线程,start 方法实际上是调用了该线程的run 方法

sendThread见 sendThread.run

eventThread 见 sendThread.run

SendThread#run

sendThread.run



因为这里暂时只分析主流程,有些分支流程就暂时先跳过了

这里主要是做了几件事,

1:zookeeper服务端连接,见sendThrad.startConnect

2:发送数据,见 ClientCnxnSocketNIO.doTransport

3:保持心跳连接,见

SendThread#startConnect

sendThrad.startConnect



因为我们这里采用的是默认的模式,在上文中已经创建了ClientCnxnSocketNIO这个对象, 实际上就是使用NIO 的方式进行连接,具体如下图,代码很简单,如果有NIO 的基础,应该很好理解,就不再深入分析

ClientCnxnSocketNIO#doTransport

ClientCnxnSocketNIO.doTransport



我们默认是client 已经和ZK server 连接成功了,,这里线程已经启动完成了,然后阻塞在select 方法上,下面我们走zookeeper.create的逻辑zookeeper.create

当执行完create后,selector 被唤醒了,同时outgoingQueue队列中存在数据,然后会执行后续发送数据的IO 操作,我们来看doIO ClientCnxnSocketNIO#doIO

Zookeeper.create

zookeeper.create

在create方法中,主要是构建了请求参数,然后调用socket 的submitRequest,提交请求服务端,详情见clientCnxn.submitRequest

ClientCnxn.submitRequest

clientCnxn.submitRequest

总结一下:这里主要干下面几件事

1: 将请求参数封装成一个Packet,然后放到outgoingQueue阻塞队列中,然sendThread 处理

2: 将当前线程wait 住, 等待完成或者请求超时后唤醒

3: 请求超时后的一些处理

下面进到ClientCnxn.queuePacket

ClientCnxn.queuePacket

ClientCnxn.queuePacket

ClientCnxnSocketNIO#doIO

ClientCnxnSocketNIO#doIO

在这个方法中主要执行两个步骤,因为代码较长,所以这一块分开进行分析

1:执行写事件



总结一下:

1:从一个outgoingQueue队列中拿到一个Packet 数据, 因为前面我们执行create 逻辑的时候,已经往outgoingQueue中放入数据了,所以这里是存在数据的

2: 如果这个Packet没有创建ByteBuffer,那么创建一个ByteBuffer,并且赋值给packet 对象的bb 属性

3: 利用socket将packet数据发送出去

4:发送完成后,将packet数据添加到一个等待服务端响应的链表中

2:执行读事件



因为zk 请求会先发4个字节作为整个报文的长度,所以会创建一个指定大小的buffer空间来接收后续请求。具体读取数据在SendThread#readResponse

SendThread#readResponse

SendThread#readResponse



总结一下:在上述代码中总共干了几件事

1: 根据返回的数据类型,会走一些其他逻辑,比如心跳,权限认证

2: 如果返回的类型是-1, 说明是监听了节点数据变动后返回的结果,那么会创建一个监听结果事件放入到eventThrad 的waitingEvents 队列中,见EventThread#queueEvent

3: 如果是普通的结果,那么会从等待响应队列pendingQueue中删除这次请求的packet

4: 根据packet 重置一些属性,比如事务id,反序列化响应信息

5: 执行结束packet 的方法,然后执行一些逻辑, 见ClientCnxn#finishPacket

ClientCnxn#finishPacket

ClientCnxn#finishPacket

EventThread#run

eventThread.run

下面来看一下事件处理线程

eventThread run 方法总共执行了两个功能

1: 循环从事件的阻塞队列中获取事件

2: 执行事件 ClientCnxn.processEvent

ClientCnxn.processEvent

ClientCnxn.processEvent

// 这里if 判断比较多,所以只展示了其中的一个同步的逻辑,有些分支逻辑是异步回调的逻辑
if (event instanceof WatcherSetEventPair) {
// each watcher will process the event
WatcherSetEventPair pair = (WatcherSetEventPair) event;
for (Watcher watcher : pair.watchers) {
try { // 在这里调用了我们传入的watcher 的process方法,实现通知
watcher.process(pair.event);
} catch (Throwable t) {
LOG.error("Error while calling watcher ", t);
}
}
}
EventThread#queueEvent

EventThread#queueEvent

Zookeeper ZAB协议-客户端源码解析的更多相关文章

  1. FileZilla客户端源码解析

    FileZilla客户端源码解析 FTP是TCP/IP协议组的协议,有指令通路和数据通路两条通道.一般来说,FTP标准命令TCP端口号是21,Port方式数据传输端口是20. FileZilla作为p ...

  2. 设置ZooKeeper服务器地址列表源码解析及扩展

    设置ZooKeeper服务器地址列表源码解析及扩展 ZooKeeper zooKeeper = new ZooKeeper("192.168.109.130:2181",SESSI ...

  3. Netty5客户端源码解析

    Netty5客户端源码解析 今天来分析下netty5的客户端源码,示例代码如下: import io.netty.bootstrap.Bootstrap; import io.netty.channe ...

  4. Feign 客户端源码解析

    Feign的使用非常简单,增加如下配置之后,便可以使用Feign进行调用.非常简单是不是.主要的工作由Feign框架完成.业务代码只提供了一个Interface, 然后由Feign动态生成代理类来实现 ...

  5. Spring Cloud系列(四):Eureka源码解析之客户端

    一.自动装配 1.根据自动装配原理(详见:Spring Boot系列(二):Spring Boot自动装配原理解析),找到spring-cloud-netflix-eureka-client.jar的 ...

  6. 第零章 dubbo源码解析目录

    第一章 第一个dubbo项目 第二章  dubbo内核之spi源码解析 2.1  jdk-spi的实现原理 2.2 dubbo-spi源码解析 第三章 dubbo内核之ioc源码解析 第四章 dubb ...

  7. [源码解析] PyTorch 分布式(2) --- 数据加载之DataLoader

    [源码解析] PyTorch 分布式(2) --- 数据加载之DataLoader 目录 [源码解析] PyTorch 分布式(2) --- 数据加载之DataLoader 0x00 摘要 0x01 ...

  8. zookeeper集群搭建及Leader选举算法源码解析

    第一章.zookeeper概述 一.zookeeper 简介 zookeeper 是一个开源的分布式应用程序协调服务器,是 Hadoop 的重要组件. zooKeeper 是一个分布式的,开放源码的分 ...

  9. dubbo源码解析-zookeeper创建节点

    前言 在之前dubbo源码解析-本地暴露中的前言部分提到了两道高频的面试题,其中一道dubbo中zookeeper做注册中心,如果注册中心集群都挂掉,那发布者和订阅者还能通信吗?在上周的dubbo源码 ...

  10. Zookeeper 源码(三)Zookeeper 客户端源码

    Zookeeper 源码(三)Zookeeper 客户端源码 Zookeeper 客户端主要有以下几个重要的组件.客户端会话创建可以分为三个阶段:一是初始化阶段.二是会话创建阶段.三是响应处理阶段. ...

随机推荐

  1. C#/VB.NET 如何在 Word 文档中添加页眉和页脚

    页眉位于文档中每个页面的顶部区域,常用于显示文档的附加信息,可以插入时间.图形.公司微标.文档标题.文件名或作者姓名等:页脚位于文档中每个页面的底部的区域,常用于显示文档的附加信息,可以在页脚中插入文 ...

  2. 红米手机刷 LineageOS (实操)

    参考:https://miuiver.com/how-to-flash-lineageos/ 实操机型:红米note8 1. 下载Android Platform-Tools 2. 下载对应机型的TW ...

  3. 解决Connecting to 127.0.0.1:8118... failed: Connection refused.

    这里是代理问题的锅 $ env|grep -I proxy http_proxy=http://127.0.0.1:8118 ftp_proxy=http://127.0.0.1:8118 https ...

  4. 【11】java之抽象类

    一.抽象类基本概念 1.1 抽象类 抽象类:是指在普通类的结构里增加抽象方法的组成部分,抽象类要使用 abstract 声明. 抽象方法:没有方法体且必须使用 abstract 关键字进行定义. 拥有 ...

  5. javascript中的二进制运算符

    javascript的二进制运算符用于直接对二进制位进行计算,好处是速度快,缺点是不直观. 位运算符只对整数起作用,如果一个运算子不是整数,则会转换成整数后再执行;我们都知道javascript的数值 ...

  6. C++ MFC字符转换

    创建Win32 空项目 字符说明:国外 1个字符对应1个字节   多字节 中文  1个字符对应对个字节  宽字节   Unicode  utf-8  3个   GBK  2个 多字节转为 宽字节    ...

  7. vue框架08 vue3

    vue3的介绍 # vue项目的版本,新项目使用vue3,部分老项目使用vue2 # vue3的变化 1.性能的提升 - 打包大小减少41% - 初次渲染快55%,更新渲染块133% - 内存减少54 ...

  8. Docker - Can't resolve instance hostname.

    Docker容器无法解析主实例主机名 在为redis集群搭建哨兵的时候遇到这个异常 解决方案:在配置文件中添加: ALLOW_EMPTY_PASSWORD=yes 参考文档:linux - redis ...

  9. 挂上burpsuite代理之后显示505 HTTP Version Not Supported 解决方法

    505 HTTP Version Not Supported  什么意思呢? HTTP505状态码代表的意思是 服务器不支持的HTTP版本,即 HTTP 505 HTTP Version Not Su ...

  10. django的模型层(二)

    django的模型层(二) 一 创建模型 from django.db import models # Create your models here. class Author(models.Mod ...