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. ...
随机推荐
- i春秋exec
打开是一个gif,提示文字未登录 话不多说,查看源码 发现vim字样,可能是文件泄露 直接在url后加/.index.php.swp来下载泄露文件 下载好了之后放vm上使用vim -r .index ...
- 在服务器上搭建Jenkins自动化部署工具
在公司发现很多时候都需要手动部署,然后有天听到自动部署这个词想着有没有什么工具能够自动部署项目,最好能自动化部署前后端生成 docker images 运行方便管理.最后经过我各种筛查,发现用 jen ...
- 【Hadoop学习】补充:优化、新特性
一.数据压缩 1.概述 原则:IO密集而不是计算密集的job 压缩算法选择 2.压缩位置选择 通过参数进行配置 3.压缩实例: 数据流的压缩和解压缩 Map输出端采用压缩 Reduce输出端采用压缩 ...
- openpyxl写数据
import osimport openpyxlos.chdir(r'D:/openpyxl') wb = openpyxl.Workbook() sht = wb.create_sheet('dat ...
- MSTN CE和MSTN SDK安装经验及技巧
MSTN CE和MSTN SDK安装经验及技巧 本文介绍了MSTN CE及MSTN CE SDK安装的流程以及在安装时可能会遇到的问题 一.MSTN CE安装 1.MSTN CE即Microstati ...
- Surp Suite入门
BurpSuite代理工具是以拦截代理的方式,拦截所有通过代理的网络流量,如客户端的请求数据.服务器端的返回信息等.Burp Suite主要拦截HTTP和HTTPS 协议的流量,通过拦截,Burp S ...
- python 数据迁移
Python数据库迁移 操作数据库 mysql uroot -p create database Python1031 charset=utf8; 数据迁移 from flask_migrate im ...
- MVP、原型、概念验证,傻傻分不清楚?
MVP.原型以及概念验证这三者的概念虽然没有密切的联系,但也有不少人会分不清这三者的区别,在这篇文章中,我们会帮大家区分一下这三个概念.首先是MVP,MVP是Minimum Viable Produc ...
- JDBC中文乱码问题
解决JDBC中文数据存入数据库乱码问题 ?useUnicode=true&characterEncoding=utf-8
- 大数据 - ADS 数据可视化实现
之前数据分层处理,最后把轻度聚合的结果保存到 ClickHouse 中,主要的目的就是提供即时的数据查询.统计.分析服务.这些统计服务一般会用两种形式展现,一种是为专业的数据分析人员的 BI 工具,一 ...