Zookeeper的watch实际上要处理两类事件
1. 连接状态事件(type=None, path=null)
这类事件不需要注册,也不需要我们连续触发,我们只要处理就行了。
2. 节点事件
节点的建立,删除,数据的修改。它是one time trigger,我们需要不停的注册触发,还可能发生事件丢失的情况。
上面2类事件都在Watch中处理,也就是重载的process(Event event)
节点事件的触发,通过函数exists,getData或getChildren来实现
这类函数,有双重作用:
- 注册触发事件
- 函数本身的功能
函数的本身的功能又可以用过异步的回调函数来实现,重载processResult()过程中处理函数本身的的功能。
函数还可以指定自己的watch,所以每个函数都有4个版本。根据自己的需要来选择不同的函数,不同的版本
全局常量定义
public class Properties{
/* ZK上所有子节点*/
public static List<String> ipNodes = new ArrayList<>(10);
/* ZK上所有子节点的数量*/
public static int ipNodeNum = ipNodes.size();
}
服务注册与服务发现
private static final String IP = HostUtils.getHostAddress();
private static final String ZK_ROOT_PATH_IP = "/home/ip-nodes";
private static CuratorFramework client = ZkClientHolder.getInstance();
/**
* 节点注册,项目启动时将本机节点的IP注册到ZK
*/
public static void init() {
try {
String currentPath = client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL)
.forPath(ZK_ROOT_PATH_IP + "/" + IP);
LocalLog.info("", "IP地址注册成功,路径为{}", currentPath);
} catch (Exception e) {
LocalLog.info("", "IP地址注册失败,节点的IP已经存在");
}
getIpNodes();
}
/**
* 服务发现,需要节点IP列表时调用此方法
*/
public static void getIpNodes() {
try {
Properties.ipNodes = client.getChildren().forPath(ZK_ROOT_PATH_IP);
Properties.ipNodeNum = EotProperties.ipNodes.size();
PathChildrenCache pathChildrenCache = new PathChildrenCache(client, ZK_ROOT_PATH_IP, true);
pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
if(event.getType().equals(PathChildrenCacheEvent.Type.INITIALIZED)){
LocalLog.info("", "子节点初始化成功");
}else if(event.getType().equals(PathChildrenCacheEvent.Type.CHILD_ADDED)) {
Properties.ipNodes = client.getChildren().forPath(ZK_ROOT_PATH_IP);
Properties.ipNodeNum = Properties.ipNodes.size();
LocalLog.info("","添加子节点路径:", event.getData().getPath());
}else if(event.getType().equals(PathChildrenCacheEvent.Type.CHILD_REMOVED)){
roperties.ipNodes = client.getChildren().forPath(ZK_ROOT_PATH_IP);
roperties.ipNodeNum = Properties.ipNodes.size();
LocalLog.info("","删除子节点:", event.getData().getPath());
}else if(event.getType().equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)){
Properties.ipNodes = client.getChildren().forPath(ZK_ROOT_PATH_IP);
Properties.ipNodeNum = Properties.ipNodes.size();
LocalLog.info("","修改子节点数据:", new String(event.getData().getData()));
}
}
});
pathChildrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
} catch (Exception e) {
LocalLog.info("", "IP地址发现失败,未发现" + ZK_ROOT_PATH_IP + "下的节点", e);
}
}
Watcher 设置是开发中最常见的,需要搞清楚watcher的一些基本特征,对于exists、getdata、getchild对于节点的不同操作会收到不同的 watcher信息
state=-112 会话超时状态
state= -113 认证失败状态
state= 1 连接建立中
state= 2 (暂时不清楚如何理解这个状态,ZOO_ASSOCIATING_STATE)
state=3 连接已建立状态
state= 999 无连接状态
type=1 创建节点事件
type=2 删除节点事件
type=3 更改节点事件
type=4 子节点列表变化事件
type= -1 会话session事件
type=-2 监控被移除事件
对父节点的变更以及孙节点的变更都不会触发watcher,而对watcher本身节点以及子节点的变更会触发watcher,具体参照下表。
操作 | 方法 | 触发watcher | watcher state | watcher type | watcher path |
Create当前节点 | getdata | × | × | × | × |
getchildren | √ | 3 | 4 | √ | |
exists | × | × | × | × | |
set当前节点 | getdata | √ | 3 | 3 | √ |
getchildren | × | × | × | × | |
exists | √ | 3 | 3 | √ | |
delete当前节点 | getdata | √ | 3 | 2 | √ |
getchildren | √ | 3 | 2 | √ | |
exists | √ | 3 | 2 | √ | |
create子节点 | getdata | × | × | × | × |
getchildren | √ | 3 | 4 | √ | |
exists | × | × | × | × | |
set子节点 | getdata | × | × | × | × |
getchildren | × | × | × | × | |
exists | × | × | × | × | |
delete子节点 | getdata | × | × | × | × |
getchildren | √ | 3 | 4 | √ | |
exists | × | × | × | × | |
恢复连接 | getdata | √ | 1 | -1 | × |
getchildren | √ | 1 | -1 | × | |
exists | √ | 1 | -1 | × | |
恢复连接session未超时 | getdata | √ | -112 | -1 | × |
getchildren | √ | -112 | -1 | × | |
exists | √ | -112 | -1 | × | |
恢复连接session超时 | getdata | √ | 3 | -1 | × |
getchildren | √ | 3 | -1 | × | |
exists | √ | 3 | -1 | × |
注:×表示否,√表示是。