zookeeper(四)核心watch和watcher

zookeeper有watch事件,是一次性触发的,当watch监视的数据发生变化时,通知设置了该watch的client,即watcher。

同样,其watcher是监听数据发送了某些变化,那就一定会有对应的事件类型,和状态类型。

事件类型:(znode节点相关的)

EventType.NodeCreated

EventType.NodeDataChanged

EventType.NodeChildrenChanged

EventType.NodeDeleted

状态类型:(是跟客户端实例相关的)

KeeperState.Disconnected

KeeperState.SyncConnected

KeeperState.AuthFailed

KeeperState.Expired

 

wather的特性:一次性,客户端串行执行,轻量。

一次性:对于ZK的watcher,你只需要记住一点:zookeeper有watch事件,是一次性触发的,当watch监视的数据发生变化时,通知设置了该watch的client,即watcher,由于zookeeper的监控都是一次性的,所以每次都必须监控。

客户端串行执行:客户端watcher回调的过程是一个串行同步的过程,这为我们保证了顺序,同时需要开发人员注意一点,千万不用因为一个watcher的处理逻辑影响了整个客户端的watcher回调。

轻量:WatcherEvent是Zookeeper整个Watcher通知机制的最小通知单元。整个单元结构只包含三部分:通知状态,事件类型和节点路径。也就是说Watcher通知非常的简单,只会告诉客户端发生了事件而不会告知其具体内容,需要客户自己去进行获取,而不会直接提供具体的数据内容。

 

我们通过一个示例,详细学习下Watcher的概念和其目的。Watcher示例:

【ZookeeperWatcher】

 

  1 package bhz.zookeeper.watcher;
  2 
  3 import java.util.List;
  4 import java.util.concurrent.CountDownLatch;
  5 import java.util.concurrent.atomic.AtomicInteger;
  6 
  7 import org.apache.zookeeper.CreateMode;
  8 import org.apache.zookeeper.WatchedEvent;
  9 import org.apache.zookeeper.Watcher;
 10 import org.apache.zookeeper.Watcher.Event.EventType;
 11 import org.apache.zookeeper.Watcher.Event.KeeperState;
 12 import org.apache.zookeeper.ZooDefs.Ids;
 13 import org.apache.zookeeper.ZooKeeper;
 14 import org.apache.zookeeper.data.Stat;
 15 
 16 /**
 17  * Zookeeper Wathcher 
 18  * 本类就是一个Watcher类(实现了org.apache.zookeeper.Watcher类)
 19  * @author(alienware)
 20  * @since 2015-6-14
 21  */
 22 public class ZooKeeperWatcherYuCong implements Watcher {
 23 
 24     /** 定义原子变量 */
 25     AtomicInteger seq = new AtomicInteger();
 26     /** 定义session失效时间 */
 27     private static final int SESSION_TIMEOUT = 10000;
 28     /** zookeeper服务器地址 */
 29     private static final String CONNECTION_ADDR = "127.0.0.1:2181";
 30     /** zk父路径设置 */
 31     private static final String PARENT_PATH = "/p";
 32     /** zk子路径设置 */
 33     private static final String CHILDREN_PATH = "/p/c";
 34     /** 进入标识 */
 35     private static final String LOG_PREFIX_OF_MAIN = "【Main】";
 36     /** zk变量 */
 37     private ZooKeeper zk = null;
 38     /**用于等待zookeeper连接建立之后 通知阻塞程序继续向下执行 */
 39     private CountDownLatch connectedSemaphore = new CountDownLatch(1);
 40 
 41     /**
 42      * 创建ZK连接
 43      * @param connectAddr ZK服务器地址列表
 44      * @param sessionTimeout Session超时时间
 45      */
 46     public void createConnection(String connectAddr, int sessionTimeout) {
 47         this.releaseConnection();
 48         try {
 49             //this表示把当前对象进行传递到其中去(也就是在主函数里实例化的new ZooKeeperWatcher()实例对象)
 50             zk = new ZooKeeper(connectAddr, sessionTimeout, this);
 51             System.out.println(LOG_PREFIX_OF_MAIN + "开始连接ZK服务器");
 52             connectedSemaphore.await();
 53         } catch (Exception e) {
 54             e.printStackTrace();
 55         }
 56     }
 57 
 58     /**
 59      * 关闭ZK连接
 60      */
 61     public void releaseConnection() {
 62         if (this.zk != null) {
 63             try {
 64                 this.zk.close();
 65             } catch (InterruptedException e) {
 66                 e.printStackTrace();
 67             }
 68         }
 69     }
 70 
 71     /**
 72      * 创建节点
 73      * @param path 节点路径
 74      * @param data 数据内容
 75      * @return 
 76      */
 77     public boolean createPath(String path, String data, boolean needWatch) {
 78         try {
 79             //设置监控(由于zookeeper的监控都是一次性的所以 每次必须设置监控)
 80             this.zk.exists(path, needWatch);
 81             System.out.println(LOG_PREFIX_OF_MAIN + "节点创建成功, Path: " + 
 82                                this.zk.create(    /**路径*/ 
 83                                                    path, 
 84                                                    /**数据*/
 85                                                    data.getBytes(), 
 86                                                    /**所有可见*/
 87                                                    Ids.OPEN_ACL_UNSAFE, 
 88                                                    /**永久存储*/
 89                                                    CreateMode.PERSISTENT ) +     
 90                                ", content: " + data);
 91         } catch (Exception e) {
 92             e.printStackTrace();
 93             return false;
 94         }
 95         return true;
 96     }
 97 
 98     /**
 99      * 读取指定节点数据内容
100      * @param path 节点路径
101      * @return
102      */
103     public String readData(String path, boolean needWatch) {
104         try {
105             System.out.println("读取数据操作...");
106             return new String(this.zk.getData(path, needWatch, null));
107         } catch (Exception e) {
108             e.printStackTrace();
109             return "";
110         }
111     }
112 
113     /**
114      * 更新指定节点数据内容
115      * @param path 节点路径
116      * @param data 数据内容
117      * @return
118      */
119     public boolean writeData(String path, String data) {
120         try {
121             System.out.println(LOG_PREFIX_OF_MAIN + "更新数据成功,path:" + path + ", stat: " +
122                                 this.zk.setData(path, data.getBytes(), -1));
123         } catch (Exception e) {
124             e.printStackTrace();
125             return false;
126         }
127         return true;
128     }
129 
130     /**
131      * 删除指定节点
132      * 
133      * @param path
134      *            节点path
135      */
136     public void deleteNode(String path) {
137         try {
138             this.zk.delete(path, -1);
139             System.out.println(LOG_PREFIX_OF_MAIN + "删除节点成功,path:" + path);
140         } catch (Exception e) {
141             e.printStackTrace();
142         }
143     }
144 
145     /**
146      * 判断指定节点是否存在
147      * @param path 节点路径
148      */
149     public Stat exists(String path, boolean needWatch) {
150         try {
151             return this.zk.exists(path, needWatch);
152         } catch (Exception e) {
153             e.printStackTrace();
154             return null;
155         }
156     }
157 
158     /**
159      * 获取子节点
160      * @param path 节点路径
161      */
162     private List<String> getChildren(String path, boolean needWatch) {
163         try {
164             System.out.println("读取子节点操作...");
165             return this.zk.getChildren(path, needWatch);
166         } catch (Exception e) {
167             e.printStackTrace();
168             return null;
169         }
170     }
171 
172     /**
173      * 删除所有节点
174      */
175     public void deleteAllTestPath(boolean needWatch) {
176         if(this.exists(CHILDREN_PATH, needWatch) != null){
177             this.deleteNode(CHILDREN_PATH);
178         }
179         if(this.exists(PARENT_PATH, needWatch) != null){
180             this.deleteNode(PARENT_PATH);
181         }        
182     }
183     
184     /**
185      * 收到来自Server的Watcher通知后的处理。
186      */
187     @Override
188     public void process(WatchedEvent event) {
189         
190         System.out.println("进入 process 。。。。。event = " + event);
191         
192         try {
193             Thread.sleep(200);
194         } catch (InterruptedException e) {
195             e.printStackTrace();
196         }
197         
198         if (event == null) {
199             return;
200         }
201         
202         // 连接状态
203         KeeperState keeperState = event.getState();
204         // 事件类型
205         EventType eventType = event.getType();
206         // 受影响的path
207         String path = event.getPath();
208         //原子对象seq 记录进入process的次数
209         String logPrefix = "【Watcher-" + this.seq.incrementAndGet() + "】";
210 
211         System.out.println(logPrefix + "收到Watcher通知");
212         System.out.println(logPrefix + "连接状态:\t" + keeperState.toString());
213         System.out.println(logPrefix + "事件类型:\t" + eventType.toString());
214 
215         if (KeeperState.SyncConnected == keeperState) {
216             // 成功连接上ZK服务器
217             if (EventType.None == eventType) {
218                 System.out.println(logPrefix + "成功连接上ZK服务器");
219                 connectedSemaphore.countDown();
220             } 
221             //创建节点
222             else if (EventType.NodeCreated == eventType) {
223                 System.out.println(logPrefix + "节点创建");
224                 try {
225                     Thread.sleep(100);
226                 } catch (InterruptedException e) {
227                     e.printStackTrace();
228                 }
229             } 
230             //更新节点
231             else if (EventType.NodeDataChanged == eventType) {
232                 System.out.println(logPrefix + "节点数据更新");
233                 try {
234                     Thread.sleep(100);
235                 } catch (InterruptedException e) {
236                     e.printStackTrace();
237                 }
238             } 
239             //更新子节点
240             else if (EventType.NodeChildrenChanged == eventType) {
241                 System.out.println(logPrefix + "子节点变更");
242                 try {
243                     Thread.sleep(3000);
244                 } catch (InterruptedException e) {
245                     e.printStackTrace();
246                 }
247             } 
248             //删除节点
249             else if (EventType.NodeDeleted == eventType) {
250                 System.out.println(logPrefix + "节点 " + path + " 被删除");
251             }
252             else {
253                 System.out.println(logPrefix + "其他事件:" + eventType);
254             };
255         } 
256         else if (KeeperState.Disconnected == keeperState) {
257             System.out.println(logPrefix + "与ZK服务器断开连接");
258         } 
259         else if (KeeperState.AuthFailed == keeperState) {
260             System.out.println(logPrefix + "权限检查失败");
261         } 
262         else if (KeeperState.Expired == keeperState) {
263             System.out.println(logPrefix + "会话失效");
264         }
265         else {
266             System.out.println(logPrefix + "其他状态:" + keeperState);
267         };
268 
269         System.out.println("--------------------------------------------");
270 
271     }
272 
273     /**
274      * <B>方法名称:</B>测试zookeeper监控<BR>
275      * <B>概要说明:</B>主要测试watch功能<BR>
276      * @param args
277      * @throws Exception
278      */
279     public static void main(String[] args) throws Exception {
280 
281         //建立watcher //当前客户端可以称为一个watcher 观察者角色
282         ZooKeeperWatcherYuCong zkWatch = new ZooKeeperWatcherYuCong();
283         //创建连接 
284         zkWatch.createConnection(CONNECTION_ADDR, SESSION_TIMEOUT);
285         System.out.println(zkWatch.zk.toString());
286         
287         Thread.sleep(1000);
288         
289         // 清理节点
290         zkWatch.deleteAllTestPath(false);
291         
292         //-----------------第一步: 创建父节点 /p ------------------------//
293         if (zkWatch.createPath(PARENT_PATH, System.currentTimeMillis() + "", false)) {
294             
295             Thread.sleep(1000);
296             
297             //-----------------第二步: 读取节点 /p 和    读取/p节点下的子节点(getChildren)的区别 --------------//
298             // 读取数据
299             zkWatch.readData(PARENT_PATH, false);
300         
301             // 读取子节点(监控childNodeChange事件)
302             zkWatch.getChildren(PARENT_PATH, false);
303 
304             // 更新数据
305             zkWatch.writeData(PARENT_PATH, System.currentTimeMillis() + "");
306             Thread.sleep(1000);
307             
308             // 创建子节点
309             zkWatch.createPath(CHILDREN_PATH, System.currentTimeMillis() + "", false);
310             
311             
312             //-----------------第三步: 建立子节点的触发 --------------//
313 //            zkWatch.createPath(CHILDREN_PATH + "/c1", System.currentTimeMillis() + "", true);
314 //            zkWatch.createPath(CHILDREN_PATH + "/c1/c2", System.currentTimeMillis() + "", true);
315             
316             //-----------------第四步: 更新子节点数据的触发 --------------//
317             //在进行修改之前,我们需要watch一下这个节点:
318             Thread.sleep(1000);
319             zkWatch.readData(CHILDREN_PATH, true);
320             zkWatch.writeData(CHILDREN_PATH, System.currentTimeMillis() + "");
321             
322         }
323         
324         Thread.sleep(10000);
325         // 清理节点
326         zkWatch.deleteAllTestPath(false);
327         
328         
329         Thread.sleep(10000);
330         zkWatch.releaseConnection();
331         
332     }
333 
334 }

 

posted @ 2017-10-31 22:39  喻聪  阅读(2267)  评论(0编辑  收藏  举报