Zookeeper之Zookeeper的Client的分析

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模式
时间: 2024-08-22 15:04:38

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

Zookeeper之Zookeeper底层客户端架构实现原理(转载)

Zookeeper的Client直接与用户打交道,是我们使用Zookeeper的interface.了解ZK Client的结构和工作原理有利于我们合理的使用ZK,并能在使用中更早的发现问题.本文将在研究源码的技术上讲述ZK Client的工作原理及内部工作机制. 在看完ZK Client的大致架构以后我希望能有一种简单的方式描述ZK Client的基本结构,想来想去我觉得还是图片比较能反映情况,于是我画了这张大致的结构图:   我想既然我画了这张图,就让我们从这张图开始讲起吧. 模块: 我们可

笃行杂记之Zookeeper SessionTimeOut分析

0.前言 本文为笃行日常工作记录,烂笔头系列. 源码前面,了无秘密 - by 侯杰 近期的一个C++项目里使用了Zookeeper做服务发现,期间遇到了SessionTimeOut问题的困扰,明明通过zookeeper c client设置了超时时间,但无效. 请原谅我一开始对zookeeper不熟悉.最终通过分析源码了解到SessionTimeOut最终的确定是一个协商的过程,而不是简单的配置生效. 在这里记录下Session超时时间的有关分析,基于zookeeper 3.4.8 1.zook

zookeeper运维

尽管zookeeper在编程上有很多的阱陷,API也非常的难用,但zookeeper服务本身可以说是很牢靠的了,所以在网上貌似关于运维的文章比较少. 但省心并不代表不会出麻烦,下面总结下zookeeper运维相关的东东. 重要的参考资料 这里有一个很好的Pdf,介绍了很多zookeeper的东东,作者是zookeeper的committer之一: http://www.infoq.com/presentations/Misconfiguration-ZooKeeper 另外,这里有一个总结:ht

Spark技术内幕:Master基于ZooKeeper的High Availability(HA)源码实现

     如果Spark的部署方式选择Standalone,一个采用Master/Slaves的典型架构,那么Master是有SPOF(单点故障,Single Point of Failure).Spark可以选用ZooKeeper来实现HA.      ZooKeeper提供了一个Leader Election机制,利用这个机制可以保证虽然集群存在多个Master但是只有一个是Active的,其他的都是Standby,当Active的Master出现故障时,另外的一个Standby Maste

深入解读zookeeper一致性原理

一.ZooKeeper 的实现 1.1 ZooKeeper处理单点故障 我们知道可以通过ZooKeeper对分布式系统进行Master选举,来解决分布式系统的单点故障,如图所示. 图 1.1 ZooKeeper解决单点故障   那么我们继续分析一下,ZooKeeper通过Master选举来帮助分布式系统解决单点故障,保证该系统中每时每刻只有一个Master为分布式系统提供服务.也就是说分布式的单点问题交给了ZooKeeper来处理,不知道大家此时有没有发现一个问题--"故障转移到了ZooKeep

ZooKeeper基本原理

ZooKeeper简介 ZooKeeper是一个开放源码的分布式应用程序协调服务,它包含一个简单的原语集,分布式应用程序可以基于它实现同步服务,配置维护和命名服务等.   ZooKeeper设计目的 1.最终一致性:client不论连接到哪个Server,展示给它都是同一个视图,这是zookeeper最重要的性能. 2.可靠性:具有简单.健壮.良好的性能,如果消息m被到一台服务器接受,那么它将被所有的服务器接受. 3.实时性:Zookeeper保证客户端将在一个时间间隔范围内获得服务器的更新信息

zookeeper OOM问题排查

背景 最近折腾的数据库同步项目中,大量使用了zookeeper(版本3.3.3),可以说是强依赖,但是最近频频出现zookeeper内存使用率达到100%,而且是GC不掉,直接导致整个系统挂起,伤不起阿   分析 因为大部分的情况都是无法GC回收,所以很大程度上怀疑出现memory leak. 设置了jvm参数,收集了一下OOM导致jvm crash之后的日志文件进行分析 1.-XX:+HeapDumpOnOutOfMemoryError leak分析:    从leak分析来看,比较明显,99

Zookeeper开源客户端框架Curator简介与示例

简介         Curator最初由Netflix的Jordan Zimmerman开发, Curator提供了一套Java类库, 可以更容易的使用ZooKeeper.         所谓ZooKeeper技巧(ZooKeeper Recipes),也可以称之为解决方案, 或者叫实现方案, 是指ZooKeeper的使用方法, 比如分布式的配置管理, Leader选举等         Curator作为Apache ZooKeeper天生配套的组件.ZooKeeper的Java开发者自然

基于资源编排在专有网络环境下快速部署高可用的Dubbox服务(ZooKeeper版)

本文将介绍在专有网络VPC(Virtual Private Cloud)下,基于资源编排服务,快速部署高可用的Dubbox服务的过程.Dubbox服务采用的注册中心是ZooKeeper集群.做这件事情的意义在于:节约部署Dubbox的时间,降低部署Dubbox过程中出错的风险. ROS阿里云资源编排(Resource Orchestration)是一种简单易用的云计算资源管理和自动化运维服务.用户通过模板描述多个云计算资源的依赖关系.配置等,并自动完成所有资源的创建和配置,以达到自动化部署.运维