轻量级RPC设计与实现第二版
在上一个版本中利用netty实现了简单的一对一的RPC,需要手动设置服务地址,限制性较大。
在本文中,利用zookeeper作为服务注册中心,在服务端启动时将本地的服务信息注册到zookeeper中,当客户端发起远程服务调用时,先从zookeeper中获取该服务的地址,然后根据获得的这个地址来利用netty进行网络传送。
在服务端和注册中心之间需要建立监听,当服务信息发生变化或网络连接等问题时需要对注册中心的服务信息进行修改。在本文中创建了服务注册监控中心,利用心跳机制来判断与服务端是否有较稳定的连接,当出现网络不稳定时,则从注册中心中删除属于该服务端的服务信息。在本项目中设定在5分钟内3次以上没有发送心跳包为不稳定状态。
关于心跳机制,之前有一篇文章介绍过:Dubbo心跳机制
zookeeper注册中心
zookeeper是hadoop中的一个重要组件,其主要是作为分布式协调服务
zookeeper采用节点树的数据模型,类似linux文件系统。
每个节点称做一个ZNode,每个ZNode都可以通过路径唯一标识,同时每个节点还可以存储少量数据。
本项目借鉴dubbo的注册中心模型来设计本文的注册中心。
总体上设计了四级节点,在一个节点是一个持久节点/register
,表示是记录注册服务的区域。二级节点是服务接口名,三级节点是远程服务ip地址,该节点是临时节点,节点存储的数据是具体的实现类名。
在客户端会根据服务接口名在注册中心进行查找,得到远程服务ip地址,并根据节点中存储的具体实现类名进行反射。
首先进行zookeeper初始化,利用了CuratorFramework
有关类
private static void init() {
RetryPolicy retryPolicy = new RetryNTimes(ZKConsts.RETRYTIME, ZKConsts.SLEEP_MS_BEWTEENR_RETRY);
client = CuratorFrameworkFactory.builder().connectString(ZKConsts.ZK_SERVER_PATH)
.sessionTimeoutMs(ZKConsts.SESSION_TIMEOUT_MS).retryPolicy(retryPolicy)
.namespace(ZKConsts.WORK_SPACE).build();
client.start();
}
服务的注册代码
public static void register(URL url) {
try {
String interfaceName = url.getInterfaceName();
String implClassName = url.getImplClassName();
Stat stat = client.checkExists().forPath(getPath(interfaceName, url.toString()));
if (stat != null) {
System.out.println("该节点已存在!");
client.delete().forPath(getPath(interfaceName, url.toString()));
}
client.create()
.creatingParentsIfNeeded()
.withMode(CreateMode.EPHEMERAL)
//权限控制,任何连接的客户端都可以操作该属性znode
.withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
.forPath(getPath(interfaceName, url.toString()), implClassName.getBytes());
System.out.println(getPath(interfaceName, url.toString()));
} catch (Exception e) {
e.printStackTrace();
}
}
根据服务接口名来获取远程服务连接地址
public static URL random(String interfaceName) {
try {
System.out.println("开始查找服务节点:" + getPath(interfaceName));
List<String> urlList = client.getChildren().forPath("/" + interfaceName);
System.out.println("结果:" + urlList);
String serviceUrl = urlList.get(0);
String[] urls = serviceUrl.split(":");
String implClassName = get(interfaceName, serviceUrl);
return new URL(urls[0], Integer.valueOf(urls[1]), interfaceName, implClassName);
} catch (Exception e) {
System.out.println(e);
e.printStackTrace();
}
return null;
}
注册中心与服务端进行连接时需要判断是否维持了稳定的连接,如果服务端出现宕机等情况时需要从注册中心中删除这些服务。
以前的一些处理机制,有session机制和wacher机制。
session机制
每个zookeeper注册中心与服务端进行连接时会创建一个session,在设置的sessionTimeout内,服务端会与注册中心进行心跳包的定时发送,从而感知每个客户端是否宕机,如果创建某个临时Znode节点对应的session销毁时,相应的临时节点也会被注册中心删除。
watcher机制
针对每个节点的操作,都有要给监督者进行watcher,当监控的某个节点发生了变化,则会触发watcher事件。注册中心的watcher是一次性的,触发后会被销毁。父节点,子节点增删改都能够触发watcher。触发销毁后,下次需要监听时还需要再注册一次。
本文心跳机制
服务端定时向注册中心发送本机地址,看作心跳数据包,而注册中心监控则维持一个channelId和具体地址的map,并且通过IdleHandler监听空闲事件,到达一定的空闲次数则认为不活跃,当不活跃时,zookeeper删除对应的url节点。该版本实现了上面的内容,后续的步骤在以后的版本实现。
如果10s内没有触发读,就会执行userEventTriggered
方法。如果5分钟中出现两次不活跃次数,就认定该连接不稳定,注册中心会移除属于该服务端的服务。你也可以根据实际情况设定不稳定标准。
service.scheduleAtFixedRate(() -> {
if (future.channel().isActive()) {
int time = new Random().nextInt(5);
log.info("本次定时任务获取的随机数:{}", time);
if (time > 3) {
log.info("发送本地地址到注册中心:{}", url);
future.channel().writeAndFlush(url);
}
}
}, 60, 60, TimeUnit.SECONDS);
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent state = (IdleStateEvent)evt;
if (state.state().equals(IdleState.READER_IDLE)) {
log.info("读空闲");
} else if (state.state().equals(IdleState.WRITER_IDLE)) {
log.info("写空闲");
}
//在一定时间内读写空闲才会关闭链接
else if (state.state().equals(IdleState.ALL_IDLE)) {
if (++inActiveCount == 1) {
start = System.currentTimeMillis();
}
int minute = (int)((System.currentTimeMillis() - start) / (60 * 1000)) + 1;
log.info("第{}次读写都空闲,计时分钟数{}", inActiveCount, minute);
if (inActiveCount > 2 && minute < 5) {
log.info("移除不活跃ip");
removeAndClose(ctx);
} else {
if (minute >= 5) {
log.info("新周期开始");
start = 0;
inActiveCount = 0;
}
}
}
}
}
具体实现代码:RPC第二版
轻量级RPC设计与实现第二版的更多相关文章
- 轻量级RPC设计与实现第三版
在前两个版本中,每次发起请求一次就新建一个netty的channel连接,如果在高并发情况下就会造成资源的浪费,这时实现异步请求就十分重要,当有多个请求线程时,需要设计一个线程池来进行管理.除此之外, ...
- 轻量级RPC设计与实现第五版(最终版)
在最近一段时间里,通过搜集有关资料加上自己的理解,设计了一款轻量级RPC,起了一个名字lightWeightRPC.它拥有一个RPC常见的基本功能.主要功能和特点如下: 利用Spring实现依赖注入与 ...
- 轻量级RPC设计与实现第四版
在本版本中引入了SPI机制,关于Java的SPI机制与Dubbo的SPI机制在以前的文章中介绍过. 传送门:Dubbo的SPI机制与JDK机制的不同及原理分析 因为设计的RPC框架是基于Spring的 ...
- 轻量级RPC设计与实现第一版
什么是RPC RPC (Remote Procedure Call Protocol), 远程过程调用,通俗的解释就是:客户端在不知道调用细节的情况下,调用存在于远程计算机上的某个对象,就像调用本地应 ...
- 《FPGA设计技巧与案例开发详解-第二版》全套资料包
本人参与写的一本书(TimeQuest一章由我所写),希望大家多多支持: 全书配套资料上传各大网盘资料中附送大量源码,你值得拥有--<FPGA设计技巧与案例开发详解-第二版>全套资料包-V ...
- 微博轻量级RPC框架Motan
Motan 是微博技术团队研发的基于 Java 的轻量级 RPC 框架,已在微博内部大规模应用多年,每天稳定支撑微博上亿次的内部调用.Motan 基于微博的高并发和高负载场景优化,成为一套简单.易用. ...
- python核心编程第二版笔记
python核心编程第二版笔记由网友提供:open168 python核心编程--笔记(很详细,建议收藏) 解释器options:1.1 –d 提供调试输出1.2 –O 生成优化的字节码(生成 ...
- 微博轻量级RPC框架Motan正式开源:支撑千亿调用
支撑微博千亿调用的轻量级 RPC 框架 Motan 正式开源了,项目地址为https://github.com/weibocom/motan. 微博轻量级RPC框架Motan正式开源 Motan 是微 ...
- 浅谈Hybrid技术的设计与实现第二弹
前言 浅谈Hybrid技术的设计与实现 浅谈Hybrid技术的设计与实现第二弹 浅谈Hybrid技术的设计与实现第三弹——落地篇 接上文:浅谈Hybrid技术的设计与实现(阅读本文前,建议阅读这个先) ...
随机推荐
- Dubbo(四):深入理解Dubbo核心模型Invoker
一.Dubbo中Invoker介绍 为什么说Invoker是Dubbo核心模型呢? Invoker是Dubbo中的实体域,也就是真实存在的.其他模型都向它靠拢或转换成它,它也就代表一个可执行体,可向它 ...
- 最大流-前置push-relabel算法实现
Front Push-Relabel Algorithm 接口定义 Input:容量数组vector<vector<int>> capacity ,大小为n:源点int sou ...
- 浅谈JSONP 的工作原理
小编最近在工作中经常用到 jsonp 这个东西, 表示之前从来没用过 最近稍微研究了下 当然很多内容来源于网上 收集整理 你懂的 ~~~ 话说我们访问一个页面的时候 需要像另一个网站获取部分信息, ...
- 几个点认识Nginx服务器
Nginx 其实就是一款轻量级的 Web 服务器.反向代理服务器,由于它的内存占用少,启动极快,高并发能力强,在互联网项目中广泛应用. 那么你可能会问了:“不是说 Nginx 吗?怎么又扯出来一个 W ...
- day14 jQuery
day13-14 jQueryjQuery是对js和dom的封装,相当于一个类库使用jQuery的目的: 1:获取标签 2:修改jQuery提供的方法:http://www.php100.com/ma ...
- pycharm创建Django项目时报 AttributeError:'module' object has no attrbute 'main' 错误或者创建了就只有venv一个目录
这是因为创建项目时候没有选择合适的项目环境. 所以在创建项目的时候选择一下项目的环境,比如选择python的运行环境 这时候创建的项目就不再报 AttributeError:'module' obje ...
- centos容器yum安装JDK环境
1.yum命令安装jdk 选择版本安装 -openjdk java--openjdk-devel 或者如下命令,安装jdk1.8.0的所有文件 -openjdk*yum install -y java ...
- HttpMessageNotReadableException
HttpMessageNotReadableException 情况描述: spring boot web项目,尝试使用热部署工具. Controller只写了用来测试异常的方法, 异常处理器去捕获异 ...
- kali安装—来自重装3次,创建了8个虚拟机的老安装师
个人是有点生气的,但其实用好默认设置就很简单 我个人参考了好几个博客在这里附上链接: 1.其他人博客每步详细https://blog.csdn.net/chaootis1/article/detail ...
- python-20-迭代器是个什么东西?
前言 迭代器.生成器.装饰器都有一个“器”,但他们之间没有什么关系. 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退. 迭代器有两个基本的方法:iter() ...