zookeeper入门案例
来源:互联网 发布:学生水杯推荐知乎 编辑:程序博客网 时间:2024/05/16 12:00
1、案例描述
这是一个从从zookeeper官网获取的简单案例。请先确保你已经安装好一个zookeeper机器环境,如果没有请参考我的文章《zookeeper入门》http://blog.csdn.net/koflance/article/details/78586235。
案例基本设计要求是,监听一个zookeeper的节点路径,比如/test,如果节点路径上保存的数据发生了变更,则将数据写入到指定的本地文件中,同时启动一个本地脚本命令,比如用cat将该本地文件读取出来,显示在终端。
基本步骤如下:
- 实例化一个zookeeper客户端,并将一个哨兵(watcher)注册到该节点下;
- 实例化一个节点数据监听器(datamointor),用异步方式(zk.exists)获取节点数据的变更状态;
- 实例化一个脚本执行器(Executor),并将其注册为数据监听器(datamointor)的观察者(listener);
- 数据监听器(datamointor)收到zk客户端的变更消息,立即用异步方式(zk.exists)获取节点数据,并判断是否变更(b != preData),如果变更,则通知观察者(listener);
- 脚本执行器收到datamointor的数据变更通知,立即将获取到的节点数据写入文件(filename),写入之后启用本地脚本任务process,将内容打印出来。
2、案例代码
- Executor 脚本执行器及zookeeper客户端和哨兵
package com.xxxx.xxxx.zookeeper;import org.apache.zookeeper.KeeperException;import org.apache.zookeeper.WatchedEvent;import org.apache.zookeeper.Watcher;import org.apache.zookeeper.ZooKeeper;import java.io.*;/** * 在指定的znode路径节点上,使用DataMonitor获取节点数据或状态变更情况。 * 这个类会观察指定znode节点并保存数据在该路径上,当znode存在时,启动指定的程序;当znode节点不存在时,关闭指定的程序。 * * Created by wushiweijun on 2017/11/20. */public class Executor implements Watcher, Runnable, DataMonitor.DataMonitorListener { private final DataMonitor dm; private final ZooKeeper zk; private final String filename; private final String[] exec; /*需要执行命令的程序*/ private Process child; public static void main(String[] args) { args = "localhost:2181,localhost:2182,localhost:2183 /test /Users/wushiweijun/Documents/test/zookeeper/executor.txt cat /Users/wushiweijun/Documents/test/zookeeper/executor.txt".split(" ");// if (args.length < 4) {// System.err// .println("USAGE: Executor hostPort znode filename program [args ...]");// System.exit(2);// } String hostPort = args[0]; String znode = args[1]; String filename = args[2]; String exec[] = new String[args.length - 3]; System.arraycopy(args, 3, exec, 0, exec.length); try { new Executor(hostPort, znode, filename, exec).run(); } catch (Exception e) { e.printStackTrace(); } } /** * * @param hostPort 集群的host地址列表,多个用逗号分隔,例如host:port,host:port,host:port/app/a * @param znode 访问的节点路径 * @param filename * @param exec 执行的命令程序,例如create xxx * @throws KeeperException * @throws IOException */ public Executor(String hostPort, String znode, String filename, String exec[]) throws KeeperException, IOException { this.filename = filename; this.exec = exec; /* * 创建ZK客户端,但要注意,该对象实例化时,并不会进行连接服务端,而只是初始化连接,真正的连接是异步。 * zookeeper会随机挑选(不是按照顺序)一个hostPort进行尝试,直到找到一个可以连接成功的host或者sessionTimeout * 入参格式如下: * hostport: * host:port,host:port,host:port -- 针对集群 * host:port,host:port,host:port/app/a --针对需要初始默认的根目录情况,这个被称为chroot suffix * sessionTimeout: * 链接超时时间,单位毫秒 * watcher: * 一个哨兵回调对象,用于监听节点状态的变更 */ zk = new ZooKeeper(hostPort, 3000, this); dm = new DataMonitor(zk, znode, null, this); } public void run() { try { synchronized (this) { while (!dm.dead) { wait(); } } } catch (InterruptedException e) { } } /*************************************************************************** * WatchedEvent可以告诉你三个信息: * 1、发生了什么 * 2、在那个znode路径发生的 * 3、节点当前状态是什么 * @see org.apache.zookeeper.Watcher#process(WatchedEvent) */ @Override public void process(WatchedEvent event) { /*znode路径下节点状态变更*/ dm.process(event); } /** * 节点数据发生变更 * @param data */ public void exists(byte[] data) { if (data == null) { /*数据不存在,关闭任务*/ if (child != null) { System.out.println("Killing process"); child.destroy(); try { child.waitFor(); } catch (InterruptedException e) { } } child = null; } else { if (child != null) { /*关闭任务*/ System.out.println("Stopping child"); child.destroy(); try { child.waitFor(); } catch (InterruptedException e) { e.printStackTrace(); } } try { /*将数据写入指定文件*/ FileOutputStream fos = new FileOutputStream(filename); fos.write(data); fos.close(); } catch (IOException e) { e.printStackTrace(); } try { /*启动任务*/ System.out.println("Starting child"); child = Runtime.getRuntime().exec(exec); println(child.getInputStream(), System.out); println(child.getErrorStream(), System.err); } catch (IOException e) { e.printStackTrace(); } } } private void println(InputStream inputStream, PrintStream printStream) { new Thread(new Runnable() { @Override public void run() { InputStreamReader inputStreamReader = null; try { inputStreamReader = new InputStreamReader(inputStream); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String line = null; while ((line = bufferedReader.readLine()) != null) { printStream.println(line); } } catch (Exception e) { ; } finally { if (inputStreamReader != null) { try { inputStreamReader.close(); } catch (Exception e) { ; } } } } }).start(); } /** * 节点无权、过期、不存在等情况 * @param rc the ZooKeeper reason code */ public void closing(int rc) { synchronized (this) { notifyAll(); } }}
- DataMonitor 节点数据变更监视器
package com.xxxx.xxxx.zookeeper;import org.apache.zookeeper.*;import org.apache.zookeeper.data.Stat;import java.util.Arrays;import static org.apache.zookeeper.KeeperException.Code;/** * 采用异步回调方式(zk.exists)判断获取节点的数据,并进行判断是否和之前的一致, * 不一致说明变更过,会触发listener.exist, * 如果发现节点不存在了或者节点无权限或sessiontimeout,则触发listener.close * * Created by wushiweijun on 2017/11/20. */public class DataMonitor implements Watcher, AsyncCallback.StatCallback { private ZooKeeper zk; private String znode; private DataMonitorListener listener; private Watcher chainedWatcher = null; public boolean dead; private byte[] prevData; public DataMonitor(ZooKeeper zk, String znode, Watcher chainedWatcher, DataMonitorListener listener) { this.zk = zk; this.znode = znode; this.chainedWatcher = chainedWatcher; this.listener = listener; // 异步回调方式,判断节点是否存在,如果存在则获取数据, // 如果对比之前数据发现不一致,则触发listener zk.exists(znode, true, this, null); } /** * 判断节点状态的异步回调函数 * * @param rc 返回节点状态 * @param path 节点路径 * @param ctx 上下文,即zk.exists传入的ctx参数 * @param stat 节点当前状态元数据 */ @Override public void processResult(int rc, String path, Object ctx, Stat stat) { boolean exists; switch (Code.get(rc)) { case OK: exists = true; break; case NONODE: exists = false; break; case SESSIONEXPIRED: case NOAUTH: dead = true; /*没有权限访问或者session过期,即没有ACL权限,直接关闭*/ listener.closing(rc); return; default: // 重新监听,直至有效,这样就迭代循环了 // 异步回调方式,判断节点是否存在,如果存在则获取数据, // 如果对比之前数据发现不一致,则触发listener zk.exists(znode, true, this, null); return; } byte b[] = null; if (exists) { try { /* * 如果存在, 则获取指定znode路径的数据, * 如果watch=true,则会在获取数据的同时,放一个哨兵到znode,下次有变更,则会触发 */ b = zk.getData(znode, false, null); } catch (KeeperException e) { // We don't need to worry about recovering now. The watch // callbacks will kick off any exception handling e.printStackTrace(); } catch (InterruptedException e) { return; } } if ((b == null && b != prevData) || (b != null && !Arrays.equals(prevData, b))) { /*如果数据和上次不一样,改变了*/ listener.exists(b); prevData = b; } } @Override public void process(WatchedEvent event) { String path = event.getPath(); /*判断时间类型*/ if (event.getType() == Event.EventType.None) { // 链接状态发生改变 switch (event.getState()) { case SyncConnected: // In this particular example we don't need to do anything // here - watches are automatically re-registered with // server and any watches triggered while the client was // disconnected will be delivered (in order of course) break; case Expired: // 链接失效了 dead = true; listener.closing(Code.SESSIONEXPIRED.intValue()); break; } } else { if (path != null && path.equals(znode)) { // 异步回调方式,判断节点是否存在,如果存在则获取数据, // 如果对比之前数据发现不一致,则触发listener zk.exists(znode, true, this, null); } } if (chainedWatcher != null) { chainedWatcher.process(event); } } public interface DataMonitorListener { /** * 节点数据发生变更 */ void exists(byte data[]); /** * zookeeper的会话过期或者没有节点访问权限 * * @param rc the ZooKeeper reason code */ void closing(int rc); }}
3、案例测试
- 启动Executor
- 用终端启动一个zk客户端
zkCli.sh -server localhost:2181
- 创建节点
[zk: localhost:2181(CONNECTED) 1] create /test testCreated /test
响应
Stopping childStarting childtest
- 修改节点数据
[zk: localhost:2181(CONNECTED) 2] set /test test1cZxid = 0x200000005ctime = Tue Nov 21 15:17:49 CST 2017mZxid = 0x200000008mtime = Tue Nov 21 15:19:50 CST 2017pZxid = 0x200000005cversion = 0dataVersion = 1aclVersion = 0ephemeralOwner = 0x0dataLength = 5numChildren = 0
响应
Stopping childStarting childtest1
4、参考
[1] http://zookeeper.apache.org/doc/trunk/javaExample.html
阅读全文
0 0
- zookeeper入门案例
- 02.ZooKeeper读书笔记之入门案例
- zookeeper+dubbo+dubbo 的管理界面的入门案例
- spring整合zookeeper与dubbo的入门案例(一)
- zookeeper 入门
- zookeeper 入门
- Zookeeper入门
- Zookeeper入门
- Zookeeper入门
- zookeeper入门
- ZooKeeper入门
- zookeeper入门
- ZooKeeper入门
- Zookeeper入门
- zookeeper入门
- zookeeper入门
- zookeeper入门
- Zookeeper-入门
- VS报错:”不允许对64位应用程序进行更改”,或者“Changes to 64-bit applications are not allowed.”
- Maven 精简依赖包
- 欢迎使用CSDN-markdown编辑器
- Hibernate实体类重名导致出错
- BUG
- zookeeper入门案例
- Bundle冗余分析
- 图像的插值算法之最近邻插值
- android面试题
- Jekins之——自动构建部署java maven项目(jdk1.7+tomcat7.0+jenkins2.19.3)
- centos忘记密码的解决方法
- Retrofit源码解析(一)
- Ionic 项目调试工具
- Java中的重写和重载区别