ZooKeeper学习笔记(六)之Java例程
之前小节中已经写过快速入门的例程,但在进一步了解了更多的概念之后,需要更多的实践来加深印象。总之理论学习和实践穿插进行在我看来是一种不错的方式,重要的是在参照教程编写代码的同时不要只求快速跑通,而应该与之前的看过的原理结合起来。
一个简单的Watch客户端
watch一个znode,用启动和停止特定程序的方式作出回应。
Requirements
- 接收的参数:
服务地址
需要watch的znode
可执行程序+参数 - 从zonde中取得数据并运行程序
- 如果znode变化,用新的数据重启程序
- 如果znode删除,终止程序
程序设计
通常ZooKeeper应用分解为两部分,一部分Executor
用于维护连接,另一部分DataMonitor
监控数据。(注:还记得上一节中讲到的事件线程和io线程吗?)同时,executor包含主线程和执行逻辑,它负责用户交互以及与可执行程序的交互。这可执行程序是通过参数传递进来的,它根据znode的更改而启停。
Executor类
例程的主体,包含ZooKeeper对象和DataMonitor。Executor把自己作为Watcher传递进ZooKeeper的构造方法,为此Executor必须实现Watcher接口。
|
|
Watcher接口由ZooKeeper定义,ZooKeeper通过这个接口回信给它的容器,接口只包含一个方法process(),通过它通信主线程可能感兴趣的事件,例如连接和会话的状态改变等。本例中Executor只是简单地将事件下发给DataMonitor,由它决定事件的处理。这样做主要是为了展示惯例的做法,即拥有ZooKeeper连接的Executor可以自由地将事件以新的形式(可重新封装) 委派/代理给其它对象。
|
|
至于DataMonitorListener接口则完全是为这个例程自定义的,与zooKeeper无关,DataMonitor通过它来回信给它的容器(Executor)。接口在DataMonitor中定义,在Executor中实现,方法由DataMonitor调用,但业务逻辑由Executor来决定。
接口的定义
|
|
接口在Executor中的实现
|
|
总之,这就是调用与回调:Execuotr将事件代理给DataMonitor,DataMonitor处理完自己的任务(监控数据)后通过回调接口将结果返回给上层容器Executor继续后续业务(管理可执行程序)。
DataMonitor类
DataMonitor类比较能体现ZooKeeper的逻辑,异步与事件驱动。DataMonitor通过zk.exists(znode, true, this, null)
启动业务,因为所有的处理都是在收到事件后开始的。
不要把完成回调和watch回调混淆,ZooKeeper.exists()的完成回调是DataMonitor对StatCallback.processResult()接口的实现,它在异步操作ZooKeeper.exists()完成后调用。而watch事件触发后,将被发送给Executor对象,因为在建立连接时作为Watcher传递给了ZooKeeper。
123456789 public class DataMonitor implements Watcher, StatCallback {……public void processResult(int rc, String path, Object ctx, Stat stat) {boolean exists;switch (rc) {case Code.Ok:exists = true;break;……
exists操作返回后,processResult被调用,它确认操作完成情况,读取znode数据,最后回调Executor。
运行效果
初始状态下,节点不存在,使用cli模式来创建和修改指定node,Executor收到watch事件后,将调用echo命令打印znode数据。
输入
|
|
setData的输出比较多,上两节中我们介绍过stat structure,这里是一一对应的。例如cZxid < mZxid , mtime和ctime都是时钟时间,有三种version号,node非瞬时故ephemeralOwner = 0x0。
输出
|
|
代码分析
System.arraycopy(args, 3, exec, 0, exec.length)
可用于数组Copy.
整个的流程如下所示:
实际上有两个线程在运行,一个为实现了Runnable的Executor,另一个为ZooKeeper,Executor自启动后就进行了Wait状态,直到znode被删除,DataMonitor回调Executor的closing方法它才会继续运行,此时如果dm.dead被置位,程序结束。
附完整源码
Executor
|
|
DataMonitor
|
|