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. ...
随机推荐
- css网页布局设置总结
目录 1 网页样式 1.1 引入方法 1.1.1内联样式 1.1.2内部样式表 1.1.3链接外部样式 1.1.4导入外部样式 1.2 基础语法 1.3 选择器的分类 1.3.1标记选择器 1.3.2 ...
- Mqttnet内存与性能改进录
1 MQTTnet介绍 MQTTnet是一个高性能的 .NET MQTT库,它提供MQTT客户端和MQTT服务器的功能,支持到最新MQTT5协议版本,支持.Net Framework4.5.2版本或以 ...
- 可视化—AntV G6 高亮相邻节点的两种方式
目录 内置的高亮节点 自定义高亮 自定义高亮时保持原始颜色 总结 案例完整代码 通过官方文档,可知高亮相邻节点分为两种方法,文档描述并不是很清楚,对刚接触这个库的小白并不是很友好,慢慢总结慢慢来吧 内 ...
- 如何配置 SLO
前言 无论是对外提供 IaaS PaaS SaaS 的云公司,还是提供信息技术服务的乙方公司,亦或是金融 制造等各行各业的数据中心.运维部门,我们的一个非常重要的合同承诺或考核评估指标就是:SLA(即 ...
- JDBC中文乱码问题
解决JDBC中文数据存入数据库乱码问题 ?useUnicode=true&characterEncoding=utf-8
- [python] CairoSVG使用教程
1 CairoSVG介绍 代码下载地址 CairoSVG是一个将SVG1.1转为PNG,PDF, PS格式的转化.SVG算目前火热的图像文件格式了,它的英文全称为Scalable Vector Gra ...
- Ajax+WCF+MySQL实现数据库部署并调用
最近的数据库课程要求将MySQL数据库部署在服务器上,参考了大佬们的博客后,总结一下. 先放上参考的大佬们的博客. [原创经验分享]JQuery(Ajax)调用WCF服务 - 南宫萧尘 - 博客园 ...
- 原生js实现rsa加密
原生js实现rsa加密 示例 createNewUserKey().then(function(keyPairs) { encrypt("this is origin text", ...
- Java基础篇——常用类
Objec类 Test t1 = new Test(); Test t2 = new Test(); //toString方法,对某个对象的字符串输出 //getClass方法,返回该对象的Class ...
- 【深入浅出 Yarn 架构与实现】4-4 RM 管理 Application
在 YARN 中,Application 是指应用程序,它可能启动多个运行实例,每个运行实例由 -个 ApplicationMaster 与一组该 ApplicationMaster 启动的任务组成, ...