zookeeper 的Watcher机制实现

http://www.lujiaguang.com/?p=107

zookeeper机原理(长连接):
Zookeeper 并不是用来专门存储数据的,它的作用主要是用来维护和监控你存储的数据的状态变化。通过监控这些数据状态的变化,从而可以达到基于数据的集群管。当对目录节点监控状态打开时,一旦目录节点的状态发生变化,Watcher 对象的 process 方法就会被调用。
(任何实现org.apache.zookeeper.Watcher类,持有zookeeper实例对象,只要调用zookeeper的方法,并且监控相应节点类型,
  那么zookeeper服务端该节点发生变化会通知到观察者(观察者重写方法process会被调用))
zk = new ZooKeeper(zookeeperQuorum, sessionTimeout, this); 
zookeeperQuorum=IP+端口(xxx.xxx.xxx.xxx:2181,xxx.xxx.xxx.xxx:2181,xxx.xxx.xxx.xxx:2181)多个逗号隔开
可以设置观察的操作:exists,getChildren,getData
可以触发观察的操作:create,delete,setData

zookeeper观察机制;
服务端只存储事件的信息,客户端存储事件的信息和Watcher的执行逻辑.ZooKeeper客户端是线程安全的每一个应用只需要实例化一个ZooKeeper客户端即可,同一个ZooKeeper客户端实例可以在不同的线程中使用。ZooKeeper客户端会将这个Watcher对应Path路径存储在ZKWatchManager中,同时通知ZooKeeper服务器记录该Client对应的Session中的Path下注册的事件类型。当ZooKeeper服务器发生了指定的事件后,ZooKeeper服务器将通知ZooKeeper客户端哪个节点下发生事件类型,ZooKeeper客户端再从ZKWatchManager中找到相应Path,取出相应watcher引用执行其回调函数process
源码分析如:
zookeeper.java
 wcb = new DataWatchRegistration(watcher, clientPath);//将观察者引用和节点路径绑定
ZKWatchManager.java
 public void register(int rc) {
            if (shouldAddWatch(rc)) {
                Map<String, Set<Watcher>> watches = getWatches(rc);
                synchronized(watches) {
                    Set<Watcher> watchers = watches.get(clientPath);
                    if (watchers == null) {
                        watchers = new HashSet<Watcher>();
                        watches.put(clientPath, watchers);//key为path,value为watcher引用
                    }
                    watchers.add(watcher);
                }
            }
        }

znode存储数据量最大:jute.maxbuffer默认1M

通知注意点:
1.一次性触发器(可以通过定时调用方法 get() 或者 exists()获取触发事件,手动访问触发,
client在一个节点上设置watch,随后节点内容改变,client将获取事件。当节点内容再次改变,client不会获取这个事件,除非它又执行了一次读操作并设置watch
2.发送至client,watch事件延迟
watch事件异步发送至观察者。比如说client执行一次写操作,节点数据内容发生变化,操作返回后,而watch事件可能还在发往client的路上。这种情况下,zookeeper提供有序保证:client不会得知数据变化,直到它获取watch事件。网络延迟或其他因素可能导致不同client在不同时刻获取watch事件和操作返回值。
循环调用:
Watcher watcher = loopWatch?this:null;
byte[] newdata = keeper.getData(path, watcher, null);//实现改变再次提醒!!


应用场景:
Zookeeper 从设计模式角度来看,是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,Zookeeper 就将负责通知已经在 Zookeeper 上注册的那些观察者做出相应的反应。
通知者模式:
发布者持有订阅者的句柄引用,一单状态变化,调用订阅者接口,实现通知模式调用。
zookeeper 操作API:
客户端要连接 Zookeeper 服务器可以通过创建 org.apache.zookeeper. ZooKeeper 的一个实例对象,然后调用这个类提供的接口来和服务器交互
ZooKeeper 主要是用来维护和监控一个目录节点树中存储的数据的状态。
 

目录节点类型:
PERSISTENT:持久化目录节点,这个目录节点存储的数据不会丢失
PERSISTENT_SEQUENTIAL:顺序自动编号的目录节点,这种目录节点会根据当前已近存在的节点数自动加 1
EPHEMERAL:临时目录节点,一旦创建这个节点的客户端与服务器端口也就是 session 超时或者断口,这种节点会被自动删除
EPHEMERAL_SEQUENTIAL:临时自动编号节点

节点状态(Stat):
  private long czxid;
  private long mzxid;
  private long ctime;
  private long mtime;
  private int version;
  private int cversion;
  private int aversion;
  private long ephemeralOwner;
  private int dataLength;
  private int numChildren;
  private long pzxid;

Watch事件类型
ZOO_CREATED_EVENT:节点创建事件,需要watch一个不存在的节点,当节点被创建时触发,此watch通过zoo_exists()设置
ZOO_DELETED_EVENT:节点删除事件,此watch通过zoo_exists()或zoo_get()设置
ZOO_CHANGED_EVENT:节点数据改变事件,此watch通过zoo_exists()或zoo_get()设置
ZOO_CHILD_EVENT:子节点列表改变事件,此watch通过zoo_get_children()或zoo_get_children2()设置
ZOO_SESSION_EVENT:会话失效事件,客户端与服务端断开或重连时触发
ZOO_NOTWATCHING_EVENT:watch移除事件,服务端出于某些原因不再为客户端watch节点时触发
 
 
ZooKeeper 基本的操作示例
 
 
 
// 创建一个与服务器的连接
 ZooKeeper zk = new ZooKeeper("localhost:" + CLIENT_PORT, 
        ClientBase.CONNECTION_TIMEOUT, new Watcher() { 
            // 监控所有被触发的事件
            public void process(WatchedEvent event) { 
                System.out.println("已经触发了" + event.getType() + "事件!"); 
            } 
        }); 
 // 创建一个目录节点
 zk.create("/testRootPath", "testRootData".getBytes(), Ids.OPEN_ACL_UNSAFE,
   CreateMode.PERSISTENT); 
 // 创建一个子目录节点
 zk.create("/testRootPath/testChildPathOne", "testChildDataOne".getBytes(),
   Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT); 
 System.out.println(new String(zk.getData("/testRootPath",false,null))); 
 // 取出子目录节点列表
 System.out.println(zk.getChildren("/testRootPath",true)); 
 // 修改子目录节点数据
 zk.setData("/testRootPath/testChildPathOne","modifyChildDataOne".getBytes(),-1);
 System.out.println("目录节点状态:["+zk.exists("/testRootPath",true)+"]"); 
 // 创建另外一个子目录节点
 zk.create("/testRootPath/testChildPathTwo", "testChildDataTwo".getBytes(), 
   Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT); 
 System.out.println(new String(zk.getData("/testRootPath/testChildPathTwo",true,null))); 
 // 删除子目录节点
 zk.delete("/testRootPath/testChildPathTwo",-1); 
 zk.delete("/testRootPath/testChildPathOne",-1); 
 // 删除父目录节点
 zk.delete("/testRootPath",-1); 
 // 关闭连接
 zk.close();
}
输出的结果如下:
已经触发了 None 事件!
 testRootData 
 [testChildPathOne] 
目录节点状态:[5,5,1281804532336,1281804532336,0,1,0,0,12,1,6] 
已经触发了 NodeChildrenChanged 事件!
 testChildDataTwo 
已经触发了 NodeDeleted 事件!

已经触发了 NodeDeleted 事件!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值