1)几个重要概念

  • ZooKeeper:客户端入口

  • Watcher:客户端注册的callback

  • ZooKeeper.SendThread: IO线程

  • ZooKeeper.EventThread: 事件处理线程,处理各类消息callback

  • ClientCnxnSocketNIO:继承自ClientCnxnSocket,专门处理IO

2)zookeeper初始化

    • 应用提供watch实例

    • 实例化zookeeper

      • 实例化socket,默认使用ClientCnxnSocketNIO,可通过zookeeper.clientCnxnSocket配置定制

      • 实例化ClientCnxn

      • 实例化SendThread

      • 实例化EventThread

    • 启动zookeeper

      • 启动SendThread

        • 连接服务器(见SendThread.startConnect)

          • 产生真正的socket,见ClientCnxnSocketNIO.createSock

          • 向select注册一个OP_CONNECT事件并连接服务器,由于是非阻塞连接,此时有可能并不会立即连上,如果连上就会调用SendThread.primeConnection初始化连接来注册读写事件,否则会在接下来的轮询select获取连接事件中处理

          • 复位socket的incomingBuffer

          • 连接成功后会产生一个connect型的请求发给服务,用于获取本次连接的sessionid

          • 进入循环等待来自应用的请求,如果没有就根据时间来ping 服务器

    • 启动EventThread

      • 开始进入无限循环,从队列waitingEvents中获取事件,如果没有就阻塞等待

3)以一个请求为例以 zk.exists("/root", false)为例

  • 客户端线程

    • 构造一个exists类型的请求,请求类型见ZooDefs.OpCode

    • 将请求构造成一个Packet,并将该packet放入outgoingQueue

      • 唤醒select

    • 阻塞等待结果

  • SendThread

    • 通过select 轮询判断是否有socket准备好,如果能读就读,能写就写

    • 此时socket准备好写了 ,就从outgoingQueue获取packet, 将packet发送到服务端

    • 一旦发送了一个完整的packet,就将packet从outgoingQueue移除

    • 最后将packet加入到pendingQueue

    • 再次select轮询看是否有响应数据,如果有首先都去4个字节的响应头(包含响应的长度信息),然后在下一次遍历中都去响应体

    • 都到响应将packet从pendingQueue移除

    • 如果该请求packet带有一个callback,那么会将此packet放入waitingEvents队列,让EventThread去处理

    • 最后会调用p.notifyAll()解锁,于是应用线程从阻塞中出来

  • 如果使用了带callback 的exists,EventThread会干活

4)小结

4.1)

SendThread也并非完全对应与请求/响应模式,SendThread也会接受到节点变化的通知,此时客户端变成了服务端

4.2)时间和超时的控制

ClientCnxnSocket作为ClientCnxnSocketNIO的父类,

有3个关键的时间字段

  • now :每次轮询select之前更新,或者发生错误是在catch段中更新为当前时间

  • lastHeard:在读取了响应,包括上面提到的connect型请求和常规命令型请求的响应以及完成网络连接时更新为当前时间

  • lastSend:每次发送完ping 命令和请求以及完成网络连接时更新为当前时间

有下面几个超时设置

  • sessionTimeout:zookeeper初始化时设置的

  • readTimeout:sessionTimeout * 2 / 3

  • connectTimeout:sessionTimeout / hostProvider.size();  //hostProvider.size()为zookeeper服务器个数

  • getIdleRecv():now - lastHeard

  • getIdleSend():now - lastSend

  • SessionTimeout的计算

    • 如果没有完成连接to=connectTimeout - getIdleRecv()

    • 如果完成连接to=readTimeout - getIdleRecv()

    • 如果to<=0  就会抛出SessionTimeoutException

4.3)什么时候ping

计算timeToNextPing = readTimeout / 2-getIdleSend()

如果timeToNextPing <= 0,发送ping请求(只是将ping请求放入outgoingQueue,并不发生IO)

4.4)select阻塞多久

如果上述的0<timeToNextPing<to,那么阻塞时长为timeToNextPing,否则为to

如果有写请求,select会被唤醒

4.5)sendThread的工作原理

该线程作为zookeeper客户端的核心部分专门负责IO处理

  • 计算select timeout(上面提到的to)

  • 检查空闲时间,有可能抛出SessionTimeoutException或者发送ping

  • 使用select轮询,获取网络事件(连接、读、写)也就是这3类

    • 如果是连接,做连接处理

    • 如果读,过程如下

      • 读取消息头,4个字节,头包含了消息体的字节数

      • 读 取消息体,分为两个大类消息,连接型消息“connect”和非连接型消息“header”,前者上面提到过就是连接完成之后发的一种消息,用于确定 sessionid, 另外前者会调用sendThread.onConnected,后者会调用sendThread.readResponse

      • 非连接型消息有分为几类

        • ping 消息

        • auth认证消息

        • 订阅的消息,即各种变化的通知,比如子节点变化、节点内容变化,由服务器推过来的消息 ,获取到这类消息或通过eventThread.queueEvent将消息推入事件队列

        • 客户端命令的response,如果此消息带有callback着通过eventThread.queuePacket推入事件队列,否者唤醒阻塞的应用线程,注意到客户端命令都会有阻塞版本和异步版本(带callback)

    • 如果是写,就从outgoingQueue获取packet,写入网络

4.6)请求中的Watcher和StatCallback的差别

两个都是callback,两者都由EventThread,但后者控制调用线程是否会阻塞等待响应

4.7)IO模型

如图

  • 没有使用传统连接池,会和zookeeper集群中的一台相连

  • 单IO线程(NIO)+事件线程,很标准的NIO模式

Zookeeper之Zookeeper的Client的分析的更多相关文章

  1. Zookeeper之Zookeeper的Client的分析【转】

    Zookeeper之Zookeeper的Client的分析 1)几个重要概念 ZooKeeper:客户端入口 Watcher:客户端注册的callback ZooKeeper.SendThread:  ...

  2. zookeeper安装使用及工作原理分析

    1. Zookeeper概念简介 Zookeeper是一个分布式协调服务:就是为用户的分布式应用程序提供协调服务,它是集群的管理者,监视着集群中各个节点的状态,根据节点提交的反馈进行下一步合理操作. ...

  3. ZooKeeper源码阅读——client(二)

    原创技术文章,转载请注明:转自http://newliferen.github.io/ 如何连接ZooKeeper集群   要想了解ZooKeeper客户端实现原理,首先需要关注一下客户端的使用方式, ...

  4. ZooKeeper日志与快照文件简单分析

    有用过Zookeeper的都知道zoo.cfg配置文件中有dataDir配置项用于存储数据,不过可能有些人不太清楚这个目录具体存储的是那些数据,默认情况下这个目录是用于存储Log(事务日志)与Snap ...

  5. ZooKeeper 01 - 什么是ZooKeeper + 部署ZooKeeper集群

    目录 1 什么是ZooKeeper 2 ZooKeeper的功能 2.1 配置管理 2.2 命名服务 2.3 分布式锁 2.4 集群管理 3 部署ZooKeeper集群 3.1 下载并解压安装包 3. ...

  6. 【ZooKeeper】ZooKeeper入门流水记

    单机模式 下载zookeeper的包 wget http://mirror.bit.edu.cn/apache/zookeeper/zookeeper-3.4.9/zookeeper-3.4.9.ta ...

  7. zookeeper(3) zookeeper的实践及原理

    一.基于java API初探zookeeper的使用 (1)建立连接 public static void main(String[] args) { //NOT_CONNECTED-->CON ...

  8. zookeeper:一.zookeeper集群安装

    1.zookeeper简介2.安装zookeeper2.1 安装环境准备2.2 安装zookeeper2.2.1.解压zookeeper压缩包到/opt/zookeeper2.2.2.编辑zookee ...

  9. Dubbo+Zookeeper(一)Zookeeper初识

    前面花了一段时间去学习SpringCloud的相关知识,主要是理解微服务的概念并使用SpringCloud的一系列组件实现微服务落地.学习这些组件本身是简单的,跟着操作一遍基本就会了,这也得益于Spr ...

  10. Zookeeper - 什么是Zookeeper,以及zookeeper的安装(1)

    Zookeeper 什么是Zookeeper? 官网传送门 ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的 ...

随机推荐

  1. Easyui入门视频教程 第05集---Easyui复杂布局

    目录 ----------------------- Easyui入门视频教程 第09集---登录完善 图标自定义   Easyui入门视频教程 第08集---登录实现 ajax button的使用  ...

  2. 在 iOS 中实现方法链调用

    编译:伯乐在线 - 林欣达 如有好文章投稿,请点击 → 这里了解详情 如需转载,发送「转载」二字查看说明 前言 链式调用(chained calls)是指在函数调用返回了一个对象的时候,使得这个调用链 ...

  3. 自动化运维之Cobbler自动化部署安装操作系统

    Cobbler概述: Cobbler可以用来快速建立 Linux 网络安装环境,它已将 Linux 网络安装的技术门槛,从大专以上文化水平,成功降低到初中以下,连补鞋匠都能学会. 在生产环境中,经常批 ...

  4. windbg dds、dps、dqs

    dds.dps和dqs命令显示给定范围内存的内容,它们是把内存区域转储出来,并把内存中每个元素都视为一个符号对其进行解析,dds是四字节视为一个符号,dqs是每8字节视为一个符号,dps是根据当前处理 ...

  5. Java8 lambda表达式10个示例

    Java 8 刚于几周前发布,日期是2014年3月18日,这次开创性的发布在Java社区引发了不少讨论,并让大家感到激动.特性之一便是随同发布的lambda表达式,它将允许我们将行为传到函数里.在Ja ...

  6. 【SqlServer】SqlServer中Alter语句的使用

    在修改Sql Server表结构时,常用到Alter语句,把一些常用的alter语句列举如下. 1:向表中添加字段 Alter table [表名] add [列名] 类型 2:  删除字段 Alte ...

  7. Swift 构造与析构

    前言 与 OC 一样,Swift 中也存在构造和析构过程.不同的是,OC 中的构造方法和析构方法只是普通的方法,而 Swift 中构造器和析构器是一种特殊的结构. 1.构造器 在 Swift 中,类或 ...

  8. 【Algorithm】快速排序

    一. 算法描述 快速排序:快速排序采用分治法进行排序,首先是分割,选取数组中的任意一个元素value(默认选用第一个),将数组划分为两段,前一段小于value,后一段大于value:然后再分别对前半段 ...

  9. golang学习笔记 ---TCMalloc

    图解 TCMalloc 前言 TCMalloc 是 Google 开发的内存分配器,在不少项目中都有使用,例如在 Golang 中就使用了类似的算法进行内存分配.它具有现代化内存分配器的基本特征:对抗 ...

  10. SharePoint 2013 Disaster Recovery——迁移内容数据库

    安装和配置SharePoint Farm时,一定要注意将内容数据库不要放在C盘,除非你的C盘能足够承受起日益增长的数据.由于在安装SQL SERVER中没有注意,我将数据库存放在默认的 C:\Prog ...