Zookeeper(一)客户端与server建立通信

1.通过new ZooKeeper()建立连接,通过多层的内部构造方法递进,最终锁定下图中的方法:

public ZooKeeper(
    String connectString,
    int sessionTimeout,
    Watcher watcher,
    boolean canBeReadOnly,
    HostProvider hostProvider,
    ZKClientConfig clientConfig
) throws IOException {
    ......
    cnxn = createConnection(
        connectStringParser.getChrootPath(),
        hostProvider,
        sessionTimeout,
        this.clientConfig,
        watcher,
        getClientCnxnSocket(),
        canBeReadOnly);
    cnxn.start();
}

聚焦最下面cnxn = createConnection()方法,返回的是ClientCnxn类的实例(ClientCnxn是zk客户端的核心类,Zookeeper类算是个入口和门面类),下面分析ClientCnxn的start()方法。

2.ClientCnxn类的start()方法:

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

sendThread和eventThread都是ClientCnxn类中的两个内部类的实例,并且两个类都是采用单开线程进行while(true)循环的工作方式。  

SendThread:理解为负责c和s端通信的相关工作内容。  
EventThread:理解为对产生事件和callback的单线程异步回调的工作。  
特此说明:
通过对SendThread和EventThread的阅读,能够看出异步模型采用的是生产者消费者模式实现。

3.接下来我们先进入SendThread的run()中看下如何与server建立连接(run() 方法很长,我们挑重点说)

public void run() {
            ......
            //state的在new ClientCnxn()中已经由NOT_CONNECTED重置为CONNECTING状态,所以定会进入循环    
            while (state.isAlive()) {
                try {
                    if (!clientCnxnSocket.isConnected()) {
                        // don't re-establish connection if we are closing
                        if (closing) {
                            break;
                        }
                        if (rwServerAddress != null) {
                            serverAddress = rwServerAddress;
                            rwServerAddress = null;
                        } else {
                            serverAddress = hostProvider.next(1000);
                        }
                        onConnecting(serverAddress);
                        //重点一:看到没有,此处开始建立连接
                        startConnect(serverAddress);
                        clientCnxnSocket.updateLastSendAndHeard();
                    }

                    if (state.isConnected()) {......} else {
                        ......
                    }

                    ......
                    //重点二:doTransprot()方法 ,此处 clientCnxnSocket的实现类为ClientCnxnSocketNIO  
                    clientCnxnSocket.doTransport(to, pendingQueue, ClientCnxn.this);
                } catch (Throwable e) {......}
            }

            ......
        }

 通过上面代码注释中‘的重点一’和‘重点二’,这两处是通过NIO的selector模型建立通信。  

4.重点一(看注释):  
startConnect(serverAddress): 

private void startConnect(InetSocketAddress addr) throws IOException {
            ......//以上忽略,是一些辅助判断和获取address的逻辑  
            //clientCnxnSocketNIO的connect(addr)
            clientCnxnSocket.connect(addr);
        }

clientCnxnSocketNIO的connect方法:  

void connect(InetSocketAddress addr) throws IOException {
        //look!!!,终于开始通过jdk NIO 的socket建立与server的连接了  
        SocketChannel sock = createSock();
        try {
            //把SocketChannel注册到selector上,并关注OP_CONNECT事件,由于是非阻塞的,不会等到连接建立完成
            registerAndConnect(sock, addr);
        } catch (UnresolvedAddressException | UnsupportedAddressTypeException | SecurityException | IOException e) {
            ......
        }
        ......
    }

5.重点二(看注释):
clientCnxnSocketNIO的doTransport方法:

void doTransport(
        int waitTimeOut,
        Queue<Packet> pendingQueue,
        ClientCnxn cnxn) throws IOException, InterruptedException {
        selector.select(waitTimeOut);
        Set<SelectionKey> selected;
        //获取有可操作的socket对象
        synchronized (this) {
            selected = selector.selectedKeys();
        }
        ......
        for (SelectionKey k : selected) {
            SocketChannel sc = ((SocketChannel) k.channel());
            //建立连接的流程看此处
            if ((k.readyOps() & SelectionKey.OP_CONNECT) != 0) {
                if (sc.finishConnect()) {
                    ......
                    //建立了主链接后的相关初始化操作,向server端发送第一个packet包,并执行sockKey.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
                    sendThread.primeConnection();
                }
            } else if ((k.readyOps() & (SelectionKey.OP_READ | SelectionKey.OP_WRITE)) != 0) {
                //此方法也很重要,发送操作命令和接收server反馈时关注此方法,后面文章中跟踪
                doIO(pendingQueue, cnxn);
            }
        }
        if (sendThread.getZkState().isConnected()) {
            if (findSendablePacket(outgoingQueue, sendThread.tunnelAuthInProgress()) != null) {
                enableWrite();
            }
        }
        selected.clear();
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值