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. JAVA HTML 以压缩包下载多文件

    Html:  利用form表单来发送下载请求 <form id ="submitForm" method="post"> </form> ...

  2. machine vision plan

    以OpenCV+C#/C++为主,Halcon+C#/C++.LabVIEW+NI Vision,其他还不了解 目前:Halcon+C# 1.完成:测量定位,表面质量检测 2.完成1后开始:OpenC ...

  3. Magento 2 instantiate object by Factory Objects

    magento2的Factory Objects是用来实例化non-injectable classes,目前还不知道什么叫non-injectable classes. 可以用它来实例化Helper ...

  4. Python方法oslo_service.loopingcall.LoopingCallDone代码示例

    Python方法oslo_service.loopingcall.LoopingCallDone代码示例 demo: from oslo_service import loopingcall def ...

  5. redis安装及性能测试

    Redis是一个开源的使用ANSI C语言编写.遵守BSD协议.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库.通常被称为数据结构服务器,因为值(value)可以是 字符串(Stri ...

  6. String,StringBuffer,StringBuillder的底层结构

    一:StringBuffer的底层 (1)线程安全的字符串操作类 (2)通过synchronized关键字声明同步方法,保证多线程环境下数据安全 public synchronized StringB ...

  7. 第五篇Scrum冲刺博客--Interesting-Corps

    第五篇Scrum冲刺博客 站立式会议 1.会议照片 2.队友完成情况 团队成员 昨日完成 今日计划 鲍鱼铭 音乐详情页面跳转.设计及布局实现设计 搜索页面以及音乐详情页面数据导入及测试 叶学涛 编写分 ...

  8. 服务器里Centos 7安装KVM,并通过KVM安装Centos 7

    一.安装KVM 1.进入系统后,检查cpu参数是否支持虚拟化: [root@localhost ~]# grep -Ei 'vmx|svm' /proc/cpuinfo 如果有出现vmx或者svm关键 ...

  9. Node.js调试相关

    如何进行Nodejs性能分析? nodejs性能最重要的两个部分:CPU耗时查看和内存泄漏排查 一,CPU相关 主要思路是两个:借助第三方的工具,以及借助v8自带的性能分析工具 借助第三方的工具 主要 ...

  10. 模拟CMOS集成电路-单级放大器增益直观理解

    我们再看辅助定理: 这里,Gm是指输出与地短接时的跨导:Rout表示当输入电压为零时的输出电阻.这个是书上的原话,但是在推算公式时发现,这两个量的定义还不是完全完整,我 的理解是: 首先Gm是等效跨导 ...