Rpc-实现Client对ZooKeeper的服务监听
1、前言
在上一篇文章中,完成了ZooKeeper注册中心。但是在上一篇中,ZooKeeper添加了一个简单的本地缓存,存在一些问题:
- 当本地缓存OK,ZooKeeper对应服务有新的实例时,本地缓存不会自动更新
- 当ZooKeeper对应服务实例关闭,本地缓存不会监控到实例消失
2、编写
之前我们是将缓存直接放在ZooKeeperClientUtils中的,维护一个Map集合。我们将缓存部分移动到ZooKeeperClientCache中,缓存数据从这里获取:
我们监听树上所有节点的变化情况,对于包含实例的变化,每次获取对应的服务信息,然后通过Clinet查询现存的对应服务的实例,进行更新。
watchPathSet维护了Client调用过的服务集合,对于调用过的服务才开启本地的缓存,并且进行更新。
instances即为本地缓存集合
@Slf4j
public class ZookeeperClientCache {
private static final Map<String, List<InetSocketAddress>> instances=new ConcurrentHashMap<>();
private static final Set<String> watchPathSet=new ConcurrentHashSet<>();
private static CuratorFramework zookeeperClient;
private static boolean isListening=false;
//将服务加入监听set中
public static void addListenService(String service){
//开启服务监听
openListen();
//path路径放入
watchPathSet.add(ZookeeperUtil.serviceName2Path(service));
}
//添加本地缓存,同时开启监听服务
public static void addLocalCache(String serviceName,List<InetSocketAddress> addressList){
//直接替换原本的缓存
instances.put(serviceName,addressList);
//将服务加入监听set
addListenService(serviceName);
}
public static void cleanLocalCache(String serviceName){
log.info("服务调用失败,清除本地缓存,重新获取实例===>{}",serviceName);
instances.remove(serviceName);
}
public static boolean containsKey(String serviceName){
return instances.containsKey(serviceName);
}
public static List<InetSocketAddress> getOrDefault(String serviceName){
return instances.getOrDefault(serviceName,null);
}
public static List<InetSocketAddress> getInstances(String serviceName){
try {
String path = ZookeeperUtil.serviceName2Path(serviceName);
//获取路径下所有的实现
List<String> instancePaths = zookeeperClient.getChildren().forPath(path);
List<InetSocketAddress> addressList = new ArrayList<>();
for (String instancePath : instancePaths) {
byte[] bytes = zookeeperClient.getData().forPath(path+"/"+instancePath);
String json = new String(bytes);
InetSocketAddress instance = InetSocketAddressSerializerUtil.getInetSocketAddressByJson(json);
addressList.add(instance);
}
return addressList;
} catch (Exception e) {
log.error("服务获取失败====>{}",e);
throw new RpcException(RpcError.SERVICE_NONE_INSTANCE);
}
}
private static synchronized void openListen(){
//已初始化过
if (isListening){
return;
}
//注入client
if (zookeeperClient==null) {
zookeeperClient=ZookeeperUtil.getZookeeperClient();
}
TreeCache cache = TreeCache.newBuilder(zookeeperClient, "/cn/zko0/myRpc/api").setCacheData(true).build();
cache.getListenable().addListener((c, event) -> {
if ( event.getData() != null )
{
System.out.println("type=" + event.getType() + " path=" + event.getData().getPath());
//可以通过event.type来进行节点的处理,我这里直接多节点每次行为做reload
if (event.getData().getPath().contains("Service/")){
//是服务节点,做更新
String path = event.getData().getPath();
//去除尾部实例段
path=path.substring(0,path.lastIndexOf("/"));
String serviceName = ZookeeperUtil.path2ServiceName(path);
if (watchPathSet.contains(path)) {
log.info("更新本地缓存");
List<InetSocketAddress> addressList = getInstances(serviceName);
addLocalCache(serviceName,addressList);
}
}
}
else
{
System.out.println("type=" + event.getType());
}
});
try {
cache.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
isListening=true;
}
}
创建完Cache类,只需要修改之前ZooKeeperClientUtils中,从当前类改为Cache类获取即可:

完整代码:
@Slf4j
public class ZookeeperClientUtils {
private static CuratorFramework client = ZookeeperUtil.getZookeeperClient();
public static InetSocketAddress searchService(String serviceName, LoadBalancer loadBalancer) {
InetSocketAddress address;
//本地缓存查询
if (ZookeeperClientCache.containsKey(serviceName)){
List<InetSocketAddress> addressList = ZookeeperClientCache.getOrDefault(serviceName);
if (!addressList.isEmpty()){
//使用lb进行负载均衡
return loadBalancer.select(addressList);
}
}
try {
String path = ZookeeperUtil.serviceName2Path(serviceName);
//获取路径下所有的实现
List<String> instancePaths = client.getChildren().forPath(path);
List<InetSocketAddress> addressList = new ArrayList<>();
for (String instancePath : instancePaths) {
byte[] bytes = client.getData().forPath(path+"/"+instancePath);
String json = new String(bytes);
InetSocketAddress instance = InetSocketAddressSerializerUtil.getInetSocketAddressByJson(json);
addressList.add(instance);
}
ZookeeperClientCache.addLocalCache(serviceName,addressList);
return loadBalancer.select(addressList);
} catch (Exception e) {
log.error("服务获取失败====>{}",e);
throw new RpcException(RpcError.SERVICE_NONE_INSTANCE);
}
}
}
3、测试
实现上述代码,下面是服务监听的简单测试
开启Server,Client:

关闭Server,Server自动进行服务的注销:

Client服务监控:

Rpc-实现Client对ZooKeeper的服务监听的更多相关文章
- Linux对外提供服务 网络操作 端口操作 1.开启服务监听端口 2.设置防火墙,放行访问端口的包 iptables&netfilter 四表五链和通堵策略
主题: Linux服务器上软件提供服务 1.网络操作 2.端口操作 1.网络操作 本机必须能够ping通目标主机(本地虚拟机或者远程主机) 2.端口操作 1.开启服务监听端口 2.设置防火墙,放行访问 ...
- C# Socket基础(一)之启动异步服务监听
本文主要是以代码为主..NET技术交流群 199281001 .欢迎加入. //通知一个或多个正在等待的线程已发生事件. ManualResetEvent manager = new ManualRe ...
- 用nodejs搭建一个简单的服务监听程序
作为一个从业三年左右的,并且从事过半年左右PHP开发工作的前端,对于后台,尤其是对以js语言进行开发的nodejs,那是比较有兴趣的,虽然本身并没有接触过相关的工作,只是自己私下做的一下小实验,但是还 ...
- mysql部署后无法远程连接的原因(错误代码10061),服务监听127.0.0.1和0.0.0.0的区别
在Ubuntu上部署mysql服务并添加了一个非root用户后,发现无法远程连接, Navicat连接mysql出现2003——can't connect to mysql server on loc ...
- Zookeeper三个监听案例
一.监听某一节点内容 /** * @author: PrincessHug * @date: 2019/2/25, 14:28 * @Blog: https://www.cnblogs.com/Hel ...
- Zookeeper Curator 事件监听 - 秒懂
目录 写在前面 1.1. Curator 事件监听 1.1.1. Watcher 标准的事件处理器 1.1.2. NodeCache 节点缓存的监听 1.1.3. PathChildrenCache ...
- zookeeper如何永久监听
转自:http://www.cnblogs.com/viviman/archive/2013/03/11/2954118.html 一 回调基础知识 znode 可以被监控,包括这个目录节点中存储的数 ...
- Zookeeper中Watcher监听实现增删改
8.1 连接方法 package com.zookeeper.day01; import org.apache.zookeeper.*; import java.io.IOException; pub ...
- ZooKeeper 笔记(2) 监听数据变化
ZK中的每个节点都可以存储一些轻量级的数据,这些数据的变化会同步到集群中的其它机器.在应用中程序员可以添加watcher来监听这些数据的变化,watcher只会触发一次,所以触发过后想要继续监听,必须 ...
- Nginx服务监听端口修改启动bug
监听的端口从80 修改到其他端口出现启动不起来问题. 解决方案如下: yum install policycoreutils-python sudo cat /var/log/audit/audit. ...
随机推荐
- 记一次 .NET 某自动化集采软件 崩溃分析
一:背景 1.讲故事 前段时间有位朋友找到我,说他的程序在客户的机器上跑着跑着会出现偶发卡死,然后就崩掉了,但在本地怎么也没复现,dump也抓到了,让我帮忙看下到底怎么回事,其实崩溃类的dump也有简 ...
- C#winform使用NOPI读取Excel读取图片
需求:在Winform使用NOPI做导入时候,需要导入数据的同时导入图片. 虽然代码方面不适用(我好像也没仔细看过代码),但是感谢大佬给了灵感http://www.wjhsh.net/IT-Ramon ...
- Git同步操作
同步github数据 先要进入仓库文件夹 新建仓库文件夹要初始化或将远程仓库clone下来 git init或git clone https://github.com/用户名称/仓库名称.git 新建 ...
- Visual Studio高版本 ArcObject for .Net 低版本
在基于ArcGIS的开发中,经常会存在Visual Studio版本高,ArcObject for .Net 版本低的问题.例如Visual Studio 2015的环境下,安装ArcObject f ...
- day41 6-1 安装配置maven & 6-2 创建maven项目 & 6-3 搭建springMVC框架 & 6-4 springMVC常用注解和封装工作单元
day41 调度器 定义 web.xml配置 控制器Controller 配置自动扫描控制器 在spring-mv.xml中加入 <!-- 启用spring mvc 的注解 --> < ...
- C++编程笔记(通信)(win32平台)
目录 一.初始化网络库 二.socket套接字 2.1服务端 2.2客户端 三.发送.接收数据 3.1发送 3.2接收数据 四.自定义的结构体 4.1 发送端 4.2接收端 IPV6版本套接字的创建 ...
- 【每日一题】【模拟】2021年11月11日--LRU 缓存机制
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 .实现 LRUCache 类: LRUCache(int capacity) 以正整数作为容量 capacity 初始化 L ...
- MIT6.828学习笔记3(Lab3)
Lab 3: User Environments 在这个lab中我们需要创建一个用户环境(UNIX中的进程,它们的接口和实现不同),加载一个程序并运行,并使内核能够处理一些常用的中断请求. Part ...
- 在Maven中出现javax.mail中文乱码问题解决记录
学习Java时,看廖雪峰大神文章使用了javax.mail来发送SMTP邮件.在加入中文时,发现收到的邮件里中文都是乱码. 按照一般经验,多半是编码的问题.然而在代码中,会涉及到编码的地方已经全部设置 ...
- (小白向)2020-12-18 中国大学MOOC第十二讲-动态变量应用
1创建单向链表(10分) 问题描述:根据随机输入的若干非零整数,以数字0结束:建立一个新链表. 输入:随机输入若干个整数,以数字0结束 输出:新建链表中个节点的值,数字间没有间隔字符. 样例:输入 5 ...