zk源码阅读15:zk client之架构会话过程概述

作者: 赤子心_d709 | 来源:发表于2017-07-02 17:12 被阅读121次

    摘要

    前面讲dataTree的时候,是从底至顶的讲解方法,这里讲zk客户端的结构,尝试用从顶至底的讲解顺序
    ,本节主要讲解zk的客户端架构,以及会话的过程,包含少量代码,主要讲解

      zk客户端的主要核心组件
      zk客户端会话过程
        初始化阶段
        创建阶段
        响应阶段
    

    具体详细的源码下几节再讲

    简介

    zk客户端的主要是以下几个核心组件

    ZooKeeper:
      客户端的入口
    ClientWatchManager:
      客户端Watcher管理器
    HostProvider:
      客户端地址列表管理器
    ClientCnxn:
      客户端核心线程,内部包含了SendThread和EventThread两个线程,
      SendThread为I/O线程,主要负责Zookeeper客户端和服务器之间的网络I/O通信;
      EventThread为事件线程,主要负责对服务端事件进行处理。
    

    架构如下:


    image.png

    下面讲解zk客户端会话过程

    zk客户端会话过程

    主要分为

    初始化阶段
    创建阶段
    响应阶段
    

    三个阶段进行进行讲解
    过程如下图所示

    zk客户端会话过程

    初始化阶段

    主要分为

      1.初始化ZooKeeper对象
            通过调用ZooKeeper的构造方法实例化一个ZooKeeper对象,在初始化过程中会创建一个客户端Watcher管理器ClientWatcherManager
     
      2.设置会话默认Watcher
          如果构造方法中传入了一个Watcher对象,那么客户端将这个Watcher对象作为默认Watcher保存到ClientWatcherManager中
     
      3.构造ZK服务器地址列表管理器HostProvider
            对于构造器中传入的服务器端地址,客户端将其保存在服务器地址列表管理器HostProvider中    
     
      4.创建并初始化客户端网络连接
        ZooKeeper客户端首先会创建一个网络连接器ClientCnxn,用来管理客户端与服务器的网络交互。
        另外,客户端在创建 ClientCnxn的同时还会初始化客户端两个核心队列outGoingQueue和pendingQueue,    
        分别作为客户端请求组发送队列和服务端 响应等待队列
     
      5.初始化SendThread和EventThread
        客户端会创建两个核心网络线程SendThread和EventThread,前者用于管理客户端和服务端之间的所有网络I/O,后者则用于进行客户端的事件处理。
        同时,客户端还会将ClientCnxnSocket分配给SendThread作为底层网络I/O处理器,并初始化EventThread的待处理事件队列waitingEvents,用于存放所有等待被客户端处理的事情。
    

    第1步:初始化ZooKeeper对象

    通过调用ZooKeeper的构造方法实例化一个ZooKeeper对象,在初始化过程中会创建一个客户端Watcher管理器ClientWatcherManager

    private final ZKWatchManager watchManager = new ZKWatchManager();
    

    ZKWatchManager 在前面讲watch的时候讲过了

    后续2-5步

    全部体现在构造函数中
    看构造函数即可

    public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher)  throws IOException
    public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly) throws IOException
    public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, long sessionId, byte[] sessionPasswd) throws IOException
    public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, long sessionId, byte[] sessionPasswd, boolean canBeReadOnly) throws IOException
    

    以第二个为例

      public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher,
                boolean canBeReadOnly)
            throws IOException
        {
            LOG.info("Initiating client connection, connectString=" + connectString
                    + " sessionTimeout=" + sessionTimeout + " watcher=" + watcher);
            watchManager.defaultWatcher = watcher;//设置会话默认Watcher
    
            ConnectStringParser connectStringParser = new ConnectStringParser(
                    connectString);//解析服务器ip:port
            HostProvider hostProvider = new StaticHostProvider(
                    connectStringParser.getServerAddresses());//构造ZK服务器地址列表管理器HostProvider
            //ClientCnxn负责zk server的交互
            cnxn = new ClientCnxn(connectStringParser.getChrootPath(),
                    hostProvider, sessionTimeout, this, watchManager,
                    getClientCnxnSocket(), canBeReadOnly);//创建并初始化客户端网络连接,初始化SendThread和EventThread
    
            cnxn.start();//启动sendThread和eventThread
        }
    

    除了最后cnxn.start()以外,构造函数上面的部分都是初始化Zookeeper对象
    具体ClientCnxn和HostProvider 相关代码后面再讲

    会话创建阶段

    6.启动SendThread和EventThread。
      SendThread首先会判断当前客户端的状态,进行一系列请理性工作,为客户端发送“会话创建”请求做准备。
      
    7.获取一个服务器地址。
      在开始创建TCP之前,SendThread首先需要获取一个Zookeeper服务器的目标地址, 这通常是从HostProvider中随机获取出一个地址,
    然后委托给ClientCnxnSocket去创建与Zookeeper服务器之间的TCP连接。
    
    8.创建TCP连接。获取一个服务器地址后,ClientCnxnSocket负责和服务器创建一个TCP长连接。
    
    9.构造ConnectRequest请求。
      在TCP连接创建完毕后,可能有的读者会认为,这样是否就说明已经和Zookeeper服务器完成连接了呢?
    其实不然,上面的步骤只是纯粹地从网络TCP层完成了客户端与服务端之间的Socket连接,但远未完成Zookeeper客户端的会话创建。
    
      SendThread会负责根据当前客户端的实际设置,构造出一个ConnectRequest请求,该请求代表了客户端试图与服务端创建一个会话。
    同时,Zookeeper客户端还会进一步将该请求包装成网络I/O层的Packet对象,放入发送队列outgoingQueue中去。
    
    10.发送请求。
      当客户端请求准备完毕后,就可以开始向服务端发送请求了。
      ClientCnxnSocket负责从outgoingQueue中取出一个待发送的Packet对象,将其序列化成ByteBuffer后,向服务端进行发送。
    

    这里体现在org.apache.zookeeper.ClientCnxn#start

      public void start() {
            sendThread.start();
            eventThread.start();
        }
    

    还是在ClientCnxn那一节讲解好了,这个类太多了

    响应处理阶段

    11.响应处理阶段
    接受服务器端响应。ClientCnxnSocket接受到服务端响应后,会首先判断当前的客户端状态是否是“已初始化”,
    如果尚未完成初始化,那么就认为该响应一定是会话创建请求的响应,直接交由readConnectResult方法来处理该响应。
    
    12.处理Response。
    ClientCnxnSocket会对接受到的服务端响应进行反序列化,得到ConnectResponse对象,并从中获取到Zookeeper服务端分配的会话SessionId。
    
    13.连接成功。
    连接成功后,一方面需要通知SendThread线程,进一步对客户端进行会话参数的设置,包括readTimeout和connectTimeout等,并更新客户端状态,
    另一方面,需要通知地址管理器HostProvider当前成功连接的服务器地址。
    
    14.生成事件:SyncConnected-None。
    为了能够让上层应用感知到会话的成功创建,SendThread会生成一个事件SyncConnected-None,
    代表客户端与服务器会话创建成功,并将该事件传递给EventThread线程。
    
    15.查询Watcher。
    EventThread线程收到事件后,会从ClientWatchManager管理器中查询出对应的Watcher,
    针对SyncConnected-None事件,那么就直接找出存储的默认Watcher,然后将其放到EventThread的watingEvents队列中去。
    
    16.处理事件。
    EventThread不断的从watingEvents队列中取出待处理的Watcher对象,然后直接调用该对象的process接口方法,以达到触发Watcher的目的。
    

    具体源码还是在ClientCnxn中再讲

    思考

    讲解方式

    这种从顶向下的讲解方式感觉不是很好,只能大概讲一下原理,过程,并不能很好的深入细节,需要再在讲细节的时候再回顾一遍

    refer

    https://my.oschina.net/pingpangkuangmo/blog/486780
    http://www.cnblogs.com/leesf456/p/6098255.html
    http://www.procedurego.com/article/106670.html
    《paxos到zk》
    copy定义的

    相关文章

      网友评论

        本文标题:zk源码阅读15:zk client之架构会话过程概述

        本文链接:https://www.haomeiwen.com/subject/esancxtx.html