ZooKeeper客户端可以对指定节点设置指定Watcher,当服务器指定节点发生变化是,客户端会收到服务器的通知,然后客户端可以执行相应Watcher的代码。

  默认ZooKeeper内置了一个watcher,用于打印收到的服务器的通知。

源码ZooKeeperMain.Watcher:

 protected void connectToZK(String newHost) throws InterruptedException, IOException {
if (zk != null && zk.getState().isAlive()) {
zk.close();
}
host = newHost;
zk = new ZooKeeper(host,
Integer.parseInt(cl.getOption("timeout")),
new MyWatcher());
} private class MyWatcher implements Watcher {
public void process(WatchedEvent event) {
if (getPrintWatches()) {
ZooKeeperMain.printMessage("WATCHER::");
ZooKeeperMain.printMessage(event.toString());
}
}
}

  在获取子节点、获取数据、获取状态可以设置Watcher,该Watcher会被存储到Packet包中,当Packet包收到响应时注册该Watcher,当收到服务器notification时,执行Watcher代码。

源码ZooKeeper.getChildren:

 public List<String> getChildren(final String path, Watcher watcher)
throws KeeperException, InterruptedException
{
//将watcher封装成childwatcher
WatchRegistration wcb = null;
if (watcher != null) {
wcb = new ChildWatchRegistration(watcher, clientPath);
} final String serverPath = prependChroot(path);
RequestHeader h = new RequestHeader();
h.setType(ZooDefs.OpCode.getChildren);
GetChildrenRequest request = new GetChildrenRequest();
request.setPath(serverPath);
request.setWatch(watcher != null);
GetChildrenResponse response = new GetChildrenResponse();
ReplyHeader r = cnxn.submitRequest(h, request, response, wcb);
if (r.getErr() != 0) {
throw KeeperException.create(KeeperException.Code.get(r.getErr()),
clientPath);
}
return response.getChildren();
}

源码ClientCnxn.submitRequest:

 public ReplyHeader submitRequest(RequestHeader h, Record request,
Record response, WatchRegistration watchRegistration)
throws InterruptedException {
ReplyHeader r = new ReplyHeader();
//watchRegistration被封装到Packet中
Packet packet = queuePacket(h, r, request, response, null, null, null,
null, watchRegistration);
synchronized (packet) {
while (!packet.finished) {
packet.wait();
}
}
return r;
}

源码ClientCnxn.queuePacket:

 Packet queuePacket(RequestHeader h, ReplyHeader r, Record request,
Record response, AsyncCallback cb, String clientPath,
String serverPath, Object ctx, WatchRegistration watchRegistration)
{
Packet packet = null;
synchronized (outgoingQueue) {
if (h.getType() != OpCode.ping && h.getType() != OpCode.auth) {
h.setXid(getXid());
}
//watchRegistration被封装到Packet中
packet = new Packet(h, r, request, response, null,
watchRegistration);
packet.cb = cb;
packet.ctx = ctx;
packet.clientPath = clientPath;
packet.serverPath = serverPath;
if (!zooKeeper.state.isAlive() || closing) {
conLossPacket(packet);
} else {
// If the client is asking to close the session then
// mark as closing
if (h.getType() == OpCode.closeSession) {
closing = true;
}
outgoingQueue.add(packet);
}
} sendThread.wakeup();
return packet;
}

当Packet包收到响应时注册该Watcher,源码ClientCnxn.finishPacket:

 private void finishPacket(Packet p) {
if (p.watchRegistration != null) {
p.watchRegistration.register(p.replyHeader.getErr());
} if (p.cb == null) {
synchronized (p) {
p.finished = true;
p.notifyAll();
}
} else {
p.finished = true;
eventThread.queuePacket(p);
}
}

当收到服务器notification时,执行Watcher代码,源码ClientCnxn.EventThread:

 private void processEvent(Object event) {
if (event instanceof WatcherSetEventPair) {
//执行watcher
WatcherSetEventPair pair = (WatcherSetEventPair) event;
for (Watcher watcher : pair.watchers) {
try {
watcher.process(pair.event);
} catch (Throwable t) {
LOG.error("Error while calling watcher ", t);
}
}
}
}

在删除节点、创建节点、获取子节点、设置数据、获取数据、获取权限、设置权限等异步操作时,可以设置CallBack回调函数,该回调对象会被存储到Packet包中,当Packet包收到响应时执行CallBack代码。

源码ZooKeeper.getChildren:

 public void getChildren(final String path, Watcher watcher,
ChildrenCallback cb, Object ctx)
{
final String clientPath = path;
WatchRegistration wcb = null;
if (watcher != null) {
wcb = new ChildWatchRegistration(watcher, clientPath);
} final String serverPath = prependChroot(clientPath); RequestHeader h = new RequestHeader();
h.setType(ZooDefs.OpCode.getChildren);
GetChildrenRequest request = new GetChildrenRequest();
request.setPath(serverPath);
request.setWatch(watcher != null);
GetChildrenResponse response = new GetChildrenResponse();
cnxn.queuePacket(h, new ReplyHeader(), request, response, cb,
clientPath, serverPath, ctx, wcb);
}

源码ClientCnxn.queuePacket:

 Packet queuePacket(RequestHeader h, ReplyHeader r, Record request,
Record response, AsyncCallback cb, String clientPath,
String serverPath, Object ctx, WatchRegistration watchRegistration)
{
Packet packet = null;
synchronized (outgoingQueue) {
if (h.getType() != OpCode.ping && h.getType() != OpCode.auth) {
h.setXid(getXid());
}
packet = new Packet(h, r, request, response, null,
watchRegistration);
packet.cb = cb;
packet.ctx = ctx;
packet.clientPath = clientPath;
packet.serverPath = serverPath;
if (!zooKeeper.state.isAlive() || closing) {
conLossPacket(packet);
} else {
// If the client is asking to close the session then
// mark as closing
if (h.getType() == OpCode.closeSession) {
closing = true;
}
outgoingQueue.add(packet);
}
} sendThread.wakeup();
return packet;
}

源码ClientCnxn.EventThread:

 private void processEvent(Object event) {
try {
Packet p = (Packet) event;
int rc = 0;
String clientPath = p.clientPath;
if (p.replyHeader.getErr() != 0) {
rc = p.replyHeader.getErr();
}
if (p.response instanceof ExistsResponse
|| p.response instanceof SetDataResponse
|| p.response instanceof SetACLResponse) {
StatCallback cb = (StatCallback) p.cb;
if (rc == 0) {
if (p.response instanceof ExistsResponse) {
cb.processResult(rc, clientPath, p.ctx,
((ExistsResponse) p.response)
.getStat());
} else if (p.response instanceof SetDataResponse) {
cb.processResult(rc, clientPath, p.ctx,
((SetDataResponse) p.response)
.getStat());
} else if (p.response instanceof SetACLResponse) {
cb.processResult(rc, clientPath, p.ctx,
((SetACLResponse) p.response)
.getStat());
}
} else {
cb.processResult(rc, clientPath, p.ctx, null);
}
} else if (p.response instanceof GetDataResponse) {
DataCallback cb = (DataCallback) p.cb;
GetDataResponse rsp = (GetDataResponse) p.response;
if (rc == 0) {
cb.processResult(rc, clientPath, p.ctx, rsp
.getData(), rsp.getStat());
} else {
cb.processResult(rc, clientPath, p.ctx, null,
null);
}
} else if (p.response instanceof GetACLResponse) {
ACLCallback cb = (ACLCallback) p.cb;
GetACLResponse rsp = (GetACLResponse) p.response;
if (rc == 0) {
cb.processResult(rc, clientPath, p.ctx, rsp
.getAcl(), rsp.getStat());
} else {
cb.processResult(rc, clientPath, p.ctx, null,
null);
}
} else if (p.response instanceof GetChildrenResponse) {
ChildrenCallback cb = (ChildrenCallback) p.cb;
GetChildrenResponse rsp = (GetChildrenResponse) p.response;
if (rc == 0) {
cb.processResult(rc, clientPath, p.ctx, rsp
.getChildren());
} else {
cb.processResult(rc, clientPath, p.ctx, null);
}
} else if (p.response instanceof GetChildren2Response) {
Children2Callback cb = (Children2Callback) p.cb;
GetChildren2Response rsp = (GetChildren2Response) p.response;
if (rc == 0) {
cb.processResult(rc, clientPath, p.ctx, rsp
.getChildren(), rsp.getStat());
} else {
cb.processResult(rc, clientPath, p.ctx, null, null);
}
} else if (p.response instanceof CreateResponse) {
StringCallback cb = (StringCallback) p.cb;
CreateResponse rsp = (CreateResponse) p.response;
if (rc == 0) {
cb.processResult(rc, clientPath, p.ctx,
(chrootPath == null
? rsp.getPath()
: rsp.getPath()
.substring(chrootPath.length())));
} else {
cb.processResult(rc, clientPath, p.ctx, null);
}
} else if (p.cb instanceof VoidCallback) {
VoidCallback cb = (VoidCallback) p.cb;
cb.processResult(rc, clientPath, p.ctx);
}
} catch (Throwable t) {
LOG.error("Caught unexpected throwable", t);
}
}
}

zookeeper 回调和Watcher的更多相关文章

  1. 详解回调函数——以JS为例解读异步、回调和EventLoop

      回调,是非常基本的概念,尤其在现今NodeJS诞生与蓬勃发展中变得更加被人们重视.很多朋友学NodeJS,学很久一直摸不着门道,觉得最后在用Express写Web程序,有这样的感觉只能说明没有学懂 ...

  2. Zookeeper学习之Watcher事件类型和ZK状态

    1.Zookeepe  Watcherr的事件类型和ZK状态. zookeeper:Watcher.ZK状态,事件类型(一)zookeeper有watch事件,是一次性触发的,当watch监视的数据发 ...

  3. 从观察者设计模式的角度理解Zookeeper中的Watcher

    前面关于Zookeeper提供的API中,可以观察到大部分接口参数似乎都是用了Wathcerz这个接口.这个在观察者模式中略有涉及,本文重点分析从观察者模式的角度分析该接口. 首先上该接口的UML图: ...

  4. Zookeeper中的watcher监听和leader选举机制

    watcher监听 什么是watcher接口 同一个事件类型在不同的通知状态中代表的含义有所不同,下图列举了常见的通知状态和事件类型. Watcher通知状态与事件类型一览 上图列举了ZooKeepe ...

  5. rabbitMq实现与zookeeper类似的watcher功能

    场景:A.B.C.D(可以是一个机器的不同进程,也可以是不同机器的进程)启动了相同的项目,使用同一个数据库.但是,如果A修改了数据库的数据,需要B.C.D在很短的时间能够知道数据库发生了修改.当然可以 ...

  6. zookeeper(5)--基于watcher原理实现带注册中心的RPC框架

    一.带版本控制的注册中心RPC框架 server端 //注册中心接口 public interface IRegisterCenter { public void register(String se ...

  7. 【第三周读书笔记】浅谈node.js中的异步回调和用js-xlsx操作Excel表格

    在初步学习了node.js之后,我发现他的时序问题我一直都很模糊不清,所以我专门学习了一下这一块. 首先我们来形象地理解一下进程和线程: 进程:CPU执行任务的模块.线程:模块中的最小单元. 例如:c ...

  8. 【练习】使用接口回调和handler实现数据加载到listview

    代码结构 布局: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xml ...

  9. UITableView回调和table相关成员方法详解

    http://blog.csdn.net/kingsley_cxz/article/details/9123959 1.UITableView的datasource实现: //回调获取每个sectio ...

随机推荐

  1. pytest封神之路第一步 tep介绍

    『 tep is a testing tool to help you write pytest more easily. Try Easy Pytest! 』 tep前身 tep的前身是接口自动化测 ...

  2. Linux基础 Day1

    Linux-Day1 1.用户登录 root用户 是一个特殊的管理账号,也可以称为超级管理员 root用户对系统有完全控制的权限 对系统的损害会无限大 在工作中,如果没有特殊的必要,尽量不要使用roo ...

  3. Hbase写入流程图

    写入流程图

  4. python中eval, exec, execfile,和compile

    eval(str [,globals [,locals ]])函数将字符串str当成有效Python表达式来求值,并返回计算结果. 同样地, exec语句将字符串str当成有效Python代码来执行. ...

  5. chrome浏览器截图

    1.F12 打开开发者工具台 2.ctrl + shift + p,弹出搜索框之后输入: full 3.选中Mobile-- Capture fullsize screenshot ,成功保存图片.

  6. 牛客网PAT练兵场-打印沙漏

    题目地址:https://www.nowcoder.com/pat/6/problem/4053 题意:模拟题 /** * Copyright(c) * All rights reserved. * ...

  7. Clayff力场(转载)

    本文转载自:http://blog.sina.com.cn/s/blog_b48a7ac30102w4km.html 注意:lammps中并没有完整的clayff势函数,需要将一下三者结合使用. an ...

  8. 4gl游标cursor

    游標有多種寫法,一種是報表里常見的 這種寫法呢,先定義一個接受sql語句的變量l_sql,而接受到的語句實際上只是一連串的字符串,還包含了4gl裡面的一些變量.寫好的l_sql裡面之所以有多個分段的雙 ...

  9. os.path获取当前路径及父路径

    import os pwd = os.getcwd() print("当前目录: " + pwd) father_path_method1 = os.path.dirname(pw ...

  10. 网站SEO优化技术转让奇人

    http://www.wocaoseo.com/thread-111-1-1.html 本月假期我排到了今天星期二,由于工作性质原因经常会熬夜,养成一种不好的习惯"睡懒觉"视为享受 ...