ZooKeeper Watcher代码实例

本文详细解析了ZooKeeper的监听机制,包括Watcher接口的实现方式、事件处理流程以及ZooKeeper API的使用方法。介绍了如何通过Watcher监听ZNode的变化,并展示了创建、获取、删除ZNode的操作示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ZooKeeper Watcher代码实例

主要的相关类和接口

Executor 实现了Wathcer接口、Runnable接口、DataMonitorListener接口。

通过connectString 监视ZooKeeper的一个ZNode。

当ZNode发生事件变化时,通过process(WatchedEvent event)方法调用DataMonitor的process(WatchedEvent event)方法。

DataMonitor实现了Wathcer接口和StatCallback接口。

DataMonitor处理ZNode发生变化时的process,以及处理StatCallback的回调方法。

Watcher接口

Watcher接口定义了process(WatchedEvent event) 方法,以及定义了接口Event。

接口Event中定义了KeeperState和EventType。

WatchedEvent

WatchedEvent由KeeperState、EventType和path组成。

它代表当前ZooKepper的连接状态,并且提供发生事件的znode路径以及时间类型。

其中KeeperState代表ZooKeeper的连接状态,分别为:

  1. Disconnected
  2. NoSyncConnected
  3. SyncConnected
  4. AuthFailed
  5. ConnectedReadOnly
  6. SaslAuthenticated
  7. Expired

EventType代表node的状态变更,分别为:

  1. None
  2. NodeCreated
  3. NodeDeleted
  4. NodeDataChanged,就算设置重复的数据也会有该事件
  5. NodeChildrenChanged
AsyncCallback接口

StatCallback接口是AsyncCallback的一种。

AsyncCallback一共定义了如下几个CallBack

  1. DataCallback
  2. ACLCallback
  3. ChildrenCallback
  4. StatCallback
  5. StringCallback
  6. VoidCallback
  7. Children2Callback

创建ZooKeeper Session

通过

ZooKeeper(String connectString, int sessionTimeout, Watcher watcher)

就可以创建Zookeeper的一个Session。

A Simple Watch Client 代码实例
过程和原理
  1. 初始化连接到ZooKeeper,并且注册一个监视器W
  2. W在接收到事件后,执行process()方法,根据事件的state()关闭或者重新启动额外的任务进程。
  3. 如果发生事件的znode和注册的znode路径一致,则调用ZooKeeper的exist()方法,然后执行StatCallback这个回调方法。
  4. 在StatCallback的回调方法中,获得znode对应的数据
  5. 如果数据存在,则执行打印出响应的结果
连接到客户端
sh zkCli.sh -server 127.0.0.1:2181 

运行后Executor.main后,

会得到一个WatchEvent,它的state为SyncConnected,type为null。

如果此时set znode 结点的数据,比如:

set /yangqi_test new-data

会得到一个WatchEvent,它的state为SyncConnected,type为NodeDataChanged 。

相应的,删除结点的时候,将获得类型为NodeDeleted 的Event。创建结点的时候为NodeCreated 。

ZooKeeper.exists()
public void exists(final String path, Watcher watcher,
        StatCallback cb, Object ctx)

其中:

interface StatCallback extends AsyncCallback {
    public void processResult(int rc, String path, Object ctx, Stat stat);
}

StatCallback 中只有一个方法processResult(),用于处理exits()方法后的回调。

ZooKeeper.getData()
public byte[] getData(String path, boolean watch, Stat stat)

用于获得指定路径的数据。



1. create

Class:org.apache.zookeeper.ZooKeeper
public String create(String path, byte[] data, List acl, CreateMode createMode) throws KeeperException, InterruptedException
public void create(String path, byte[] data, List acl, CreateMode createMode, AsyncCallback.StringCallback cb, Object ctx)
创建一个给定路径(path)的节点,并给它设置数据(data)和访问控制列表(acl)。ZooKeeper中的节点相对于文件系统中的目录结构,即是”directory”又是”regular file”。第二个create方法是create的异步版本,当创建完成时则调用异步callback。

(1). org.apache.zookeeper.data.ACL

这部分节选说说Zookeeper中的ACL
ZooKeeper通过ACL来对ZNode进行访问控制。ZooKeeper客户端为znode指定ACL列表,ZooKeeper服务器根据ACL列表判定某个请求ZNode的客户端是否有对应操作的权限。
一个ACL对象由schema:ID和Permissions组成。
a). scheme: scheme对应于采用哪种方案来进行权限管理,zookeeper实现了一个pluggable的ACL方案,可以通过扩展scheme,来扩展ACL的机制。zookeeper-3.4.4缺省支持下面几种scheme:
world: 它下面只有一个id, 叫anyone, world:anyone代表任何人,zookeeper中对所有人有权限的结点就是属于world:anyone的
auth: 它不需要id, 只要是通过authentication的user都有权限(zookeeper支持通过kerberos来进行authencation, 也支持username/password形式的authentication)
digest: 它对应的id为username:BASE64(SHA1(password)),它需要先通过username:password形式的authentication
ip: 它对应的id为客户机的IP地址,设置的时候可以设置一个ip段,比如ip:192.168.1.0/16, 表示匹配前16个bit的IP段
super: 在这种scheme情况下,对应的id拥有超级权限,可以做任何事情(cdrwa)
b). perm. ZooKeeper中有5种权限,从低位到高位分别是READ、WRITE、CREATE、DELETE和ADMIN,ACL的Permissions可以是5种权限中的1种或多种,它们的含义是:
* READ: 允许获取该节点的值和列出子节点。
* WRITE: 允许设置该节点的值。
* CREATE: 允许创建子节点。
* DELETE: 可以删除子节点。
* ADMIN: 允许为该节点设置权限。

(2). org.apache.zookeeper.CreateMode

org.apache.zookeeper.CreateMode可以设置znode是否为EPHEMERAL或者SEQUENTIAL。可以为下面四种值:
PERSISTENT 持久化目录znode
PERSISTENT_SEQUENTIAL 顺序自动编号的目录znode。这个目录节点是根据当前已存在的节点数递增。
EPHEMERAL 临时目录znode,一旦创建这个znode的客户端和服务器断开,这个节点就会自动删除。临时节点(EPHEMERAL)不能有子节点数据
EPHEMERAL_SEQUENTIAL 临时自动编号znode。

(3). zkCli命令

在zkCli中实现了create的命令封装,可以用户测试和数据管理:
create [-s] [-e] path data acl
其中”-s”表示创建一个顺序自动编号的节点,”-e”表示创建一个临时节点.默认为持久性节点
例如:
创建一个永久节点和临时节点

create  /test null
Created /test
create -e /test0 null
Created /test0

创建一个顺序自动编号的节点,ACL为使用digest(用户名:test 密码:debugo),权限为所有(rwcda)。关于digest的产生,可以参考zookeeper中 DigestAuthenticationProvider.generateDigest(String ipName)方法;通过向此方法指定原始的用户名和密码即可获得”digest”之后的字符串,比如传入”test:test”,将会得 到”test:V28q/NynI4JI3Rk54h0r8O5kMug=”,其内部原理是将”密码”部分进行MD5 + sha1操作.

create -s /test0/test null digest:test:V28q/NynI4JI3Rk54h0r8O5kMug=:rwcda
Ephemerals cannot have children: /test0/test
create -s /test/test null digest:test:V28q/NynI4JI3Rk54h0r8O5kMug=:rwcda
Created /test/test0000000000

创建一个节点,其ACL使用ip(172.19.17.0/24)只具有读权限

create /test/test1 "hello world" ip:172.19.17.0/24:r
Created /test/test1

2. exist

class:org.apache.zookeeper.ZooKeeper
public Stat exists(String path, Watcher watcher) throws KeeperException, InterruptedException
返回某个path的znode是否存在。并设置是否监控这个节点(第二个参数boolean watcher)。当第二个参数为true且这个语句执行成功时,监听器(watcher)就会在成功执行建立节点/删除节点/修改该节点数据时候被触发。
exists(String, Watcher)
重载方法,这里可以指定特定的监听器(watcher)对象。
exists(String, Watcher, AsyncCallback.StatCallback, Object)
exists(String, boolean, AsyncCallback.StatCallback, Object)
exist的异步实现

3. delete

public void delete(String path, int version) throws InterruptedException, KeeperException
删除path对应的znode,version为-1可以匹配任何版本,也就是删除这个节点所有的数据。此外,delete同样存在异步版本。
delete(String path, int version, AsyncCallback.VoidCallback cb, Object ctx)
delete的异步版本。
例如zkCli中删除某个节点:

delete /test/test1

4. getChildren

public List getChildren(String path, boolean watch) throws KeeperException, InterruptedException
获取指定path下所有的子znode,这个方法和exist一样同样可以设置watcher/指定特定的Watcher对象。

5. setData & getData

Stat setData(String path, byte[] data, int version)
当给定path的节点存在时给path设置数据,可以指定这个数据的版本号。如果version为-1则可以匹配任意版本。
void setData(String path, byte[] data, int version, AsyncCallback.StatCallback cb, Object ctx)
setData的异步版本。
byte[] getData(String path, Watcher watcher, Stat stat)
获取这个path对应的znode节点的数据,数据的版本等信息可以通过stat来指定。
void getData(String path, Watcher watcher, AsyncCallback.DataCallback cb, Object ctx)
getData的异步版本。

6. setACL和getACL

Stat setACL(String path, List acl, int version)
给某个znode节点重新设置访问权限,需要注意的是ZooKeeper中的目录节点权限都不具有传递性,父znode节点的权限不能传递给子目录节点。在create中已经介绍了ACL的设置方法,可以设置一系列ACL规则(即指定一系列ACL对象)。
void setACL(String path, List acl, int version, AsyncCallback.StatCallback cb, Object ctx)
setACL的异步版本
List getACL(String path, Stat stat)
返回某个znode节点的ACL对象的列表。
void getACL(String path, Stat stat, AsyncCallback.ACLCallback cb, Object ctx)
getACL的异步版本
例如zkCli中设置某个ACL规则:

[zk: localhost:2181(CONNECTED) 43] setAcl /test world:anyone:r
cZxid = 0xf000500ed
ctime = Wed Sep 24 15:13:29 CST 2014
......
[zk: localhost:2181(CONNECTED) 44] getAcl /test
'world,'anyone
: r

### 可能的原因分析 ZooKeeper Watcher 通过 RMI 连接失败可能涉及多个方面的问题,以下是常见的原因及其对应的解决方案: #### 1. **网络配置问题** 如果客户端无法访问 ZooKeeper 或者服务端未正确绑定到指定的 IP 和端口,则可能导致连接失败。这通常是因为防火墙设置、IP 地址错误或者端口冲突等原因引起的[^1]。 #### 2. **ZooKeeper 配置不正确** 当服务端未能成功向 ZooKeeper 注册其 IP 和端口号时,客户端将无法获取有效的地址信息来建立 RMI 连接。因此,需确认 `HelloService` 的实现是否已正确注册至 ZooKeeper 节点,并验证节点路径是否存在以及数据内容是否合法[^5]。 #### 3. **RMI 绑定异常** Java RMI 使用的是动态代理机制,在启动过程中需要完成对象导出和服务绑定操作。如果此阶段出现问题(比如超出了序列化范围的对象),则会导致后续调用失败。这里可以检查继承自 `UnicastRemoteObject` 的具体实现是否有误[^4]。 #### 4. **高可用性(HA)支持不足** 尽管引入了 ZooKeeper 来提升系统的可靠性,但如果缺乏完善的 HA 设计仍可能出现单点故障的情况。例如当主服务器宕机而备用机器尚未接管之前的时间窗口期内,整个系统处于不可用状态[^2]。 --- ### 解决方案建议 针对上述提到的各种可能性提供如下改进建议: #### A. 检查并优化网络环境 - 确认所有参与方之间的通信链路畅通无阻; - 关闭不必要的安全防护措施或调整策略允许必要的流量通行; - 测试不同主机间能否互相 ping 达通以初步判断连通状况良好与否。 #### B. 完善 ZooKeeper 数据管理流程 - 修改程序逻辑使得每次创建新实例前先尝试删除旧记录以防重复写入造成混乱; - 添加额外的日志输出便于定位实际存储位置与预期不符之处; ```java // 删除已有节点再重新创建新的临时顺序节点用于保存当前活动的服务实例详情 String path = "/rmi-service"; if(zk.exists(path, false)!=null){ zk.delete(path,-1); } zk.create(path,""+port,ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL); ``` #### C. 处理好 RMI 导出细节 - 明确哪些方法应该被暴露给外部使用者从而减少潜在风险; - 对于复杂的数据结构考虑采用定制化的编码解码方式代替默认行为; ```java public class HelloServiceImpl extends UnicastRemoteObject implements HelloService { protected HelloServiceImpl() throws RemoteException{ super(); } @Override public synchronized String sayHello(String name)throws RemoteException{ return "Hello "+name; } } ``` #### D. 构建完整的集群架构 - 利用心跳检测配合自动切换功能构建多活模式下的负载均衡体系; - 结合第三方工具如 Nginx 或 Haproxy 提供更高级别的接入层控制能力; --- ### 总结说明 综上所述,解决 ZooKeeper Watcher 通过 RMI 连接失败的关键在于全面排查各个环节存在的隐患并通过合理的手段逐一排除直至恢复正常运作为止。同时也要注意随着业务规模扩大持续改进现有技术框架适应未来需求变化的趋势。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值