1、前言

在上一篇文章中,完成了ZooKeeper注册中心。但是在上一篇中,ZooKeeper添加了一个简单的本地缓存,存在一些问题:

  1. 当本地缓存OK,ZooKeeper对应服务有新的实例时,本地缓存不会自动更新
  2. 当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的服务监听的更多相关文章

  1. Linux对外提供服务 网络操作 端口操作 1.开启服务监听端口 2.设置防火墙,放行访问端口的包 iptables&netfilter 四表五链和通堵策略

    主题: Linux服务器上软件提供服务 1.网络操作 2.端口操作 1.网络操作 本机必须能够ping通目标主机(本地虚拟机或者远程主机) 2.端口操作 1.开启服务监听端口 2.设置防火墙,放行访问 ...

  2. C# Socket基础(一)之启动异步服务监听

    本文主要是以代码为主..NET技术交流群 199281001 .欢迎加入. //通知一个或多个正在等待的线程已发生事件. ManualResetEvent manager = new ManualRe ...

  3. 用nodejs搭建一个简单的服务监听程序

    作为一个从业三年左右的,并且从事过半年左右PHP开发工作的前端,对于后台,尤其是对以js语言进行开发的nodejs,那是比较有兴趣的,虽然本身并没有接触过相关的工作,只是自己私下做的一下小实验,但是还 ...

  4. mysql部署后无法远程连接的原因(错误代码10061),服务监听127.0.0.1和0.0.0.0的区别

    在Ubuntu上部署mysql服务并添加了一个非root用户后,发现无法远程连接, Navicat连接mysql出现2003——can't connect to mysql server on loc ...

  5. Zookeeper三个监听案例

    一.监听某一节点内容 /** * @author: PrincessHug * @date: 2019/2/25, 14:28 * @Blog: https://www.cnblogs.com/Hel ...

  6. Zookeeper Curator 事件监听 - 秒懂

    目录 写在前面 1.1. Curator 事件监听 1.1.1. Watcher 标准的事件处理器 1.1.2. NodeCache 节点缓存的监听 1.1.3. PathChildrenCache ...

  7. zookeeper如何永久监听

    转自:http://www.cnblogs.com/viviman/archive/2013/03/11/2954118.html 一 回调基础知识 znode 可以被监控,包括这个目录节点中存储的数 ...

  8. Zookeeper中Watcher监听实现增删改

    8.1 连接方法 package com.zookeeper.day01; import org.apache.zookeeper.*; import java.io.IOException; pub ...

  9. ZooKeeper 笔记(2) 监听数据变化

    ZK中的每个节点都可以存储一些轻量级的数据,这些数据的变化会同步到集群中的其它机器.在应用中程序员可以添加watcher来监听这些数据的变化,watcher只会触发一次,所以触发过后想要继续监听,必须 ...

  10. Nginx服务监听端口修改启动bug

    监听的端口从80 修改到其他端口出现启动不起来问题. 解决方案如下: yum install policycoreutils-python sudo cat /var/log/audit/audit. ...

随机推荐

  1. C#winfrom调整任意控件宽和高

    在winform项目开发中,偶尔需要用到边框拖拽.度娘也没找到相关的轮子(可能是我不配,没推给我).只能自己造一个 上效果图(鼠标没录制上,问题不大) 上代码 private void Form1_L ...

  2. 关于led蓝牙控制器ble通信分析

    前言 前几天在网上买了一个led蓝牙控制器,可以用手机app通过蓝牙连接控制rgb led灯,当然这个也是属于ble通信.之前我写过一篇体重称蓝牙通信的,不过那个较为简单,数据也是靠分析出来的. 这次 ...

  3. 社论 22.10.9 优化连续段dp

    CF840C 给定一个序列 \(a\),长度为 \(n\).试求有多少 \(1\) 到 \(n\) 的排列 \(p_i\),满足对于任意的 \(2\le i\le n\) 有 \(a_{p_{i-1} ...

  4. Java框架--SSM&Oracle&Maven高级

    〇.内容介绍 一.MyBatis01:框架概述.环境搭建及入门案例.自定义框架 1.介绍 框架:封装细节,是开发中的解决方案 三层架构与SSM的关系 表示层web:SpringMVC框架 业务层ser ...

  5. 【Flume】概述及组成、入门案例、进阶(事务、拓扑结构)、不同拓扑案例、自定义、数据流监控Ganglia

    一.概述 1.定义 日志采集.聚合.传输的系统,基于流式结构 即:读取本地磁盘数据,写入HDFS或kafka 2.架构 Agent:JVM进程,以事件形式将数据送到目的地. Agent由三部分组成:S ...

  6. 【Zookeeper】结构、应用、安装部署与参数、客户端命令行操作、API应用、内部原理(选举机制、写数据、监听器)

    一.Zookeeper入门 1.概述 分布式服务管理框架(存储和管理数据) Zookeeper=文件系统+通知机制 2.特点 主从集群 半数以上,正常工作 请求顺序执行 数据更新具有原子性 3.数据结 ...

  7. 当我们的执行 java -jar xxx.jar 的时候底层到底做了什么?

    大家都知道我们常用的 SpringBoot 项目最终在线上运行的时候都是通过启动 java -jar xxx.jar 命令来运行的. 那你有没有想过一个问题,那就是当我们执行 java -jar 命令 ...

  8. 推荐一款 在线+离线数据 同步框架 Dotmim.Sync

    移动智能应用可以分为在线模式.纯离线模式与"在线+离线"混合模式.在线模式下系统数据一般存储在服务器端的大中型数据库(如 SQL Server.Oracle.MySQL 等),移动 ...

  9. uni-app 动态修改主题色

    老是碰到初版制作完成没多久,就整一出说什么要更改整个项目的色彩体系.真的是宝宝心里苦啊! 起初都是通过uni项目自带的uni.scss中定义,在替换页面上对应的css.以便于达到一次性修改整体布局的样 ...

  10. 带cookie爬取内容demo

    概述: 在爬取一些网站时,需要在headers中加入cookie才能返回数据,原因是存在反爬机制,我们需要尽可能的伪装成浏览器在访问这个url 时发送的数据包. demo演示: