ZooKeeper客户端线程模型学习

在ZooKeeper的使用过程中,大家都知道ZooKeeper客户端与服务端在建立连接时使用长连接,以此来维护客户端与服务端之间的心跳及各种命令(远程通信),那么客户端的线程模型是什么样的呢,今天我们就来一窥究竟。


一、线程模型

ZooKeeper客户端线程模型见图一。

图一

ZooKeeper客户端与服务端通信的线程模型主要由三个队列和两个线程组成。

三个队列分别为:

1) 待发送消息队列(OutgoingQueue)

该队列主要存储需要发送的消息,使用java.util.concurrent.LinkedBlockingDeque<Packet>

2) 已发送等待响应的队列(PendingQueue)

该队列主要存储已发送的消息,使用java.util.LinkedList<Packet>

3) 事件队列(EventQueue)

该队列主要储存各种事件消息,使用java.util.concurrent.LinkedBlockingQueue<Object>

两个线程分别为:

1) 消息发送线程

该线程主要维护客户端与服务端之间的消息通信以及超时重连机制。ZooKeeper有一种状态会导致该线程退出,在与服务端建立连接时,客户端会发送连接请求报文到服务端,服务端会根据请求中信息判断该连接会话是否过期,如果该连接被服务端判定为过期,该线程会退出。

2) 事件处理线程

该线程主要处理各种事件,处理事件种类详见org.apache.zookeeper.ClientCnxn.EventThread.processEvent(Objectevent)方法。

主要实现过程

1) 同步请求

①ZooKeeper客户端实例化完成后,会同时启动消息发送线程和事件处理线程。

②客户端在提交各种操作命令时都会先封装为数据包(Packet),加入到消息待发送队列队尾,再循环判断消息是否处理完成,未处理完成则同步等待。

③消息发送线程获取消息待发送队列队首消息,经由网络通讯模块发送消息,将发送消息在加入到已发送消息等待响应队列队尾。

④服务端返回结果,移除已发送消息等待响应队列队尾元素,处理返回结果,标记消息处理完成,并通知。

⑤客户端获取返回结果,继续执行业务处理。

2) 异步请求

①ZooKeeper客户端实例化完成后,会同时启动消息发送线程和事件处理线程。

②客户端在提交各种操作命令时都会先封装为数据包(Packet),并提供回调函数,然后将该消息加入到消息待发送队列队尾,继续执行其它业务。

③消息发送线程从消息待发送队列获取消息,经由网络通讯模块发送消息,将发送消息在加入到已发送消息等待响应队列队尾。

④服务端返回结果,移除已发送消息等待响应队列队尾元素,处理返回结果,标记消息处理完成,并将该消息加入到事件处理队列队尾。

⑤事件处理线程获取事件处理队列队首消息,调用回调函数处理响应结果。

二、主要类图


三、源码分析

实例化ZooKeeper客户端

类名:org.apache.zookeeper. ZooKeeper

ZooKeeper客户端构造函数分别如下:

public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher)  throws IOException
public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, ZKClientConfig conf) throws IOException

public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher,
            boolean canBeReadOnly, HostProvider aHostProvider,
            ZKClientConfig clientConfig) throws IOException {
        LOG.info("Initiating client connection, connectString=" + connectString
                + " sessionTimeout=" + sessionTimeout + " watcher=" + watcher);

        if (clientConfig == null) {
            clientConfig = new ZKClientConfig();
        }
        this.clientConfig = clientConfig;
        watchManager = defaultWatchManager();
        watchManager.defaultWatcher = watcher;
        ConnectStringParser connectStringParser = new ConnectStringParser(
                connectString);
        hostProvider = aHostProvider;

        cnxn = new ClientCnxn(connectStringParser.getChrootPath(),
                hostProvider, sessionTimeout, this, watchManager,
                getClientCnxnSocket(), canBeReadOnly);
        cnxn.start();
}

其中第一个和第二个构造函数均为重载方法,其都调用第三个构造函数,并提供相应的默认的参数。

在第三个构造函数中可以看到在实例化cnxn后,立即调用了cnxn.start()方法,启动了消息发送线程和事件处理线程。

客户端连接及IO

类名:org.apache.zookeeper.ClientCnxn.SendThread

        public void run() {
            ...... 
            while (state.isAlive()) {
                try {
                    if (!clientCnxnSocket.isConnected()) {
                        // don't re-establish connection if we are closing
                        if (closing) {
                            break;
                        }
                        startConnect();
                        clientCnxnSocket.updateLastSendAndHeard();
                    }

                    if (state.isConnected()) {
                        // determine whether we need to send an AuthFailed event.
                        ...... //权限认证
                        to = readTimeout - clientCnxnSocket.getIdleRecv();
                    } else {
                        to = connectTimeout - clientCnxnSocket.getIdleRecv();
                    }
                    
                    if (to <= 0) {
                        String warnInfo;
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值