Zookeeper watch机制原理

Zookeeper watch机制原理

准备工作

经过上一小节的学习我们知道ZookeeperServerMain是单机版的服务器主类
在这里插入图片描述

我们可以自己写一个ZkClient类

public class ZkClient {
	public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
        final CountDownLatch countDownLatch=new CountDownLatch(1);
        ZooKeeper zooKeeper=
                new ZooKeeper("localhost:2182",
                        4000, new Watcher() {
                    @Override
                    public void process(WatchedEvent event) {
                        System.out.println(event);
                        if(Event.KeeperState.SyncConnected==event.getState()){
                            //如果收到了服务端的响应事件,连接成功
                            countDownLatch.countDown();
                        }
                    }
                });
        countDownLatch.await();
        //CONNECTED
        System.out.println(zooKeeper.getState());
		
        byte[] data = zooKeeper.getData("/lry",new Watcher(){
            @Override
            public void process(WatchedEvent event) {
                System.out.println(event);
            }
        },new Stat());

        System.out.println(new String(data));
        System.in.read();
        //只有getData,getChildren,exist这三个api有watcher
    }
}

我们还需要一个控制台可以输入命令的主类,ZookeeperMain
在这里插入图片描述
进入ZookeeperMain的run方法,把如下代码注释

//                String line;
//                Method readLine = consoleC.getMethod("readLine", String.class);
//                while ((line = (String)readLine.invoke(console, getPrompt())) != null) {
//                    executeLine(line);
//                }

并且加上一行代码
在这里插入图片描述
这样先启动ZookeeperServerMain,在启动ZookeeperMain我们就可以在控制台手打命令了

watch示例

菜鸟要飞-zookeeper watch

菜鸟上说的很简洁明了,但是有一些小点限于篇幅没有说到,比如:

  1. 客户端发送给服务器的包不包括watcher对象信息?
  2. pendingQueue在watch机制中的作用?
  3. 为什么只需要request.setWatch(true),而不需要把watcher对象发送给服务器?
  4. 服务器在收到修改命令后触发triggerWatch方法发送xid=-1的事件给客户端后,客户端是如何从pendingQueue拿出watcher对象,然后回调process方法的?

在这里插入图片描述

源码解析

ps: 为了截图和阅读方便,针对源码有所改动
客户端getData之后发送数据流程
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
wcb在packet里,packet在outgoingQueue里,ClientCnxn的内部类SendThread的run方法把outgoingQueue给了ClientCnxnSocket类ClientCnxnSocketNio继承ClientCnxnSocket,所以最终ClientCnxnSocketNio负责从outgoingQueue取出packet发送部分数据给服务端。

ClientCnxn的内部类SendThread的run方法 的 doTranport() —>ClientCnxnSocketNio.doTranport()---->ClientCnxnSocketNio.doIO()

在这里插入图片描述在这里插入图片描述

下面看看服务器收到这条消息会做什么
有了前一篇博客的铺垫,我们直接进入FinalRequestProcessor,找到processRequest方法的case getData
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

到此服务器就把getData需要的数据返回给了客户端(这里我们不去看客户端接受这个数据的过程),服务端还把watch保存到map里面去了,下面我们看看服务器收到修改命令触发watch的流程

同样是FinalRequestProcessor的processRequest方法
在这里插入图片描述
最终会调用到DataTree的processTxn方法
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
至此服务器的watch就解析完毕,接下来看看客户端接到这个nodechanged事件是如何处理的

ClientCnxn的SendThread线程负责接受和发送数据,最终会调用到ClientCnxnSocketNIO的doIO方法
在这里插入图片描述
再进入ClientCnxn的readResponse的case xid=-1
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述看到这幅图其实有点奇怪,因为我们源码分析到现在都没有看到dataWatches这个map是如何把path对应的wathcer put进去的,其实这是getData后服务器返回数据给客户端,这跟客户端接受数据流程有关,我待会再附加里说这个吧。

但是除此之外,watcher就到waitingEvents队列里了,我们看看ClientCnxn的run方法

在这里插入图片描述
在这里插入图片描述

附加:说明pendingQueue作用和 客户端的ZKWatchManager类的dataWatches, childWatches, existsWatches是怎么put watcher的

接受数据地方:
ClientCnxn的SendThread的readResponse方法
完整的packet是从pendingQueue拿到的,也是它保存了watcher信息,因为watcher并没有在服务器和客户端之前传送
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
真正往dataWatches put<path,Watcher>
在这里插入图片描述

总结

watch机制确实绕来绕去的,一共要绕四次,客户端两次,服务端两次,zookeeper3.6提供了持久化watch和递归持久化watch,其中持久化做法其实非常简单
在这里插入图片描述remove改成get即可,源码也确实是这么做的,还添加了一个addWatch api,可以对不同的操作watch

©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页