Zookeeper client源码篇

Java版本的Api主要涉及到两个包:org.apache.zookeeperorg.apache.zookeeper.data

其余的包要么是Zookeeper内部使用,要么是Zookeeper服务实现的一部分。Java Client主要使用ZooKeeper这个类,这个类有两个构造方法,主要区别在于可以选择是否传入会话id和password。Zookeeper支持跨进程的会话id恢复机制。有时一个Java应用程序会把会话id和password放在稳定存储服务中,当程序重启时可以用于恢复早期的会话。当创建好一个Zookeeper对象以后,会新建两个线程,一个IO线程和一个event线程。IO线程基于Java Nio实现,所有的输入输入操作都是通过此线程来完成。而所有的事件回调通过even线程来完成。会话保持比如重连、发送心跳由IO线程来完成。同步方法的响应也在IO线程里处理。异步方法回调和监听事件的响应都是通过event线程来完成的。有几个需要注意的点:

异步方法执行和监听事件回调将按照完成顺序有序执行。调用者可以对回调事件进行任何处理,但是同一时间不会处理到其他回调。

回调不会阻塞IO线程或者同步调用的执行。

同步调用可能不会按照正确的顺序返回。举个栗子:假设客户端发起一个异步读/a请求,并且启动/a的监听器,当成功读取数据并且执行异步回调的时候,在执行逻辑里又发起了一个同步去/a的请求(虽然这种做法不推荐,但是代码逻辑是没有问题的),当两次读操作之间,如果/a数据发生了变化,客户端在获取到同步读结果之前,会收到/a发生变化的通知,但是由于事件队列被异步回调阻塞住了(客户端同一时间不会处理其他回调),导致在变更event被执行之前,同步读操作会获取到/a变化后的值。

最后,来看下客户端关闭逻辑。一旦Zookeeper被关闭或者收到了比如会话过期、授权失败等事件,则Zookeeper对象会失效。在结束时,两个线程关闭,此时不应该对zookeeper句柄再进行任何操作。


1、new ZooKeeper(String connectString,int sessionTimeout, Watcher watcher)发生的主要动作:

2、zookeeper.exists(String path,boolean watch, StatCallback cb, Object ctx)添加监听器的主要逻辑:

看源码需要具备的知识储备:java NIO,可以参考https://ifeve.com/overview/

3、代码分析

基本上所有操作入口都在org.apache.zookeeper.Zookeeper

重点看一下ClientCnxn:创建了SendThread和EventThread两个线程,用于处理I/O和event,

然后我们看下start()方法,就是启动线程

再看下sendTread的run()方法,state的默认值是NOT_CONNECTED,所以肯定能进到while里,并且由于当前没有连接,所以会进入到startConnect()方法里。


看下startConnect()方法,首先创建一个socketChanel,然后调用registerAndConnect()将通道和地址绑定。

接着看下registerAndConnect,首先像selector注册socket连接事件,然后和addr建立连接,最后primeConnection创建会话

看一下连接信息的填充方法primeConnection:主要是往outgoingQueue里写和服务端的交互指令。这里包括重新注册监听器,权限校验信息写入。最后通过enableReadWriteOnly打开读写开关

再回过头来看sendTread的run()方法。socket连接建立,并且填充好基本的数据以后,接下来就是和服务端的数据传输了,主要是doTransport方法,涉及到zookeeper通信协议,这里暂时不展开

再来看下数据操作,我们以create为例:主要封装在submitRequest里,最终会将数据存在outgoingQueue里

最后看下事件监听的部分:

首先通过exists(znode,true,watcher,Object)来启用服务端的监听机制。第一步先通过ExistsWatchRegistration将watcher和znode绑定,然后将查找指令放到outgoingQueue传送给服务端。

然后SendThread的readResponse用于处理服务端返回,里面会根据path和事件类型,将新的event入队。

void readResponse(ByteBuffer incomingBuffer)throws IOException {

WatchedEventwe =new WatchedEvent(event);

if (LOG.isDebugEnabled()) {

LOG.debug("Got " + we +" for sessionid 0x"

            + Long.toHexString(sessionId));

}

eventThread.queueEvent( we );//最后会将事件

}

接下来EventThread里run方法,通过processEvent(event)来处理event

可以看到processEvent方法里,最终会根据事件类型决定是否调用watcher.process呢还是调用statCallback.processResult()。

上述主要分析了client和server端会话建立的过程,以及具体的通信逻辑,还有event回调机制。缺少的一部分是客户端如何获取到服务端触发的事件,这个和通信协议有关,放到后面单独来看。

参考文章:https://www.jianshu.com/p/06e859181cc0

参考文章:https://blog.csdn.net/quhongwei_zhanqiu/article/details/45825975

你可能感兴趣的:(Zookeeper client源码篇)