当前位置:网站首页>几行代码搞定RPC服务注册和发现
几行代码搞定RPC服务注册和发现
2022-07-02 14:20:00 【Java知音_】
在编码前,需要跟大家说一下,整个项目是按照一个一个功能模块叠加实现的,由于文章排版不适合放大块代码,文章里我会截取最关键的代码给大家讲解,想要获取完整的代码,可以去 Github 上下载,已经正式开源了。
easy-rpc 开源地址:
https://github.com/CoderLeixiaoshuai/easy-rpc注意:源码可能会更新,记得拉取最新的。
需求分析:服务注册和发现
rpc 项目要实现的第一个功能模块就是:服务注册和发现,这个功能也是整个框架非常核心和关键的。
我们的 rpc 项目不用于生成环境,造个轮子嘛,只需要实现最基础的功能即可:
服务实例注册自己的元数据到注册中心,元数据包括:实例 ip、端口、接口描述等;
客户端实例想要调用服务端接口会先连接注册中心,发现待调用的服务端实例;
拿到多个服务端实例后,客户端会根据负载均衡算法选择一个合适的实例进行RPC调用。
需求很明确了,下面开始写代码。
引入三方依赖
市面上靠谱的注册中心还是很多的,这次打算同时兼容两种注册中心:Zookeeper 和 Nacos,是不是很良心?!在使用前需要先引入以下依赖。
与 Zookeeper 交互可以引入对应的 SDK,zkclient 是个不错的选择;JSON 序列化和反序列化可以引入 fastjson,虽然经常爆漏洞,但是国产还是得支持下:
<!-- Zookeeper 客户端 -->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
<!--Json 序列化反序列-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.80</version>
</dependency>至于 Nacos,可以直接引入官方提供的 SDK:nacos-client:
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>2.0.3</version>
</dependency>服务端实现服务注册
服务注册和发现分为两块功能:服务端注册和客户端发现,我们先来实现服务端注册功能。
定义服务注册接口
在日常的工作或者学习编码过程中,我们一定要习惯面向接口编程,这样做有助于增强代码可扩展性。
根据前面的需求描述,服务注册只需要干一件事情:服务注册,我们可以定义一个接口:ServiceRegistry,接口中定义一个方法:register,代码如下:
public interface ServiceRegistry {
/**
* 注册服务信息
*
* @param serviceInfo 待注册的服务
* @throws Exception 异常
*/
void register(ServiceInfo serviceInfo) throws Exception;
}服务向注册中心注册,注册的内容定义一个类ServiceInfo来封装。
/**
* 服务名称
*/
private String serviceName;
/**
* ip 地址
*/
private String ip;
/**
* 端口号
*/
private Integer port;
/**
* class 对象
*
*/
private Class<?> clazz;
/**
* bean 对象
*/
private Object obj;
// 省略 get set 方法……
}Zookeeper 实现服务注册
我们尝试用 Zookeeper 来实现服务注册功能,先新建一个类实现前面定义好的服务注册接口:
public class ZookeeperServiceRegistry implements ServiceRegistry {
}接下来重写register方法,主要功能包括调用 Zookeeper 接口创建服务节点和实例节点。其中服务节点是一个永久节点,只用创建一次;实例节点是临时节点,如果实例故障下线,实例节点会自动删除。
// ZookeeperServiceRegistry.java
@Override
public void register(ServiceInfo serviceInfo) throws Exception {
logger.info("Registering service: {}", serviceInfo);
// 创建 ZK 永久节点(服务节点)
String servicePath = "/com/leixiaoshuai/easyrpc/" + serviceInfo.getServiceName() + "/service";
if (!zkClient.exists(servicePath)) {
zkClient.createPersistent(servicePath, true);
}
// 创建 ZK 临时节点(实例节点)
String uri = JSON.toJSONString(serviceInfo);
uri = URLEncoder.encode(uri, "UTF-8");
String uriPath = servicePath + "/" + uri;
if (zkClient.exists(uriPath)) {
zkClient.delete(uriPath);
}
zkClient.createEphemeral(uriPath);
}代码非常简单,大家看注释就能懂了。
Nacos 实现服务注册
除了使用 Zookeeper 来实现,我们还可以使用 Nacos,跟上面一样我们还是先建一个类:
public class NacosServiceRegistry implements ServiceRegistry {
}接着编写构造方法,NacosServiceRegistry 类被实例化之后 Nacos 客户端也要连接上 Nacos 服务端。
// NacosServiceRegistry.java
public NacosServiceRegistry(String serverList) throws NacosException {
// 使用工厂类创建注册中心对象,构造参数为 Nacos Server 的 ip 地址,连接 Nacos 服务器
naming = NamingFactory.createNamingService(serverList);
// 打印 Nacos Server 的运行状态
logger.info("Nacos server status: {}", naming.getServerStatus());
}获得NamingService类的实例对象后,就可以调用实例注册接口完成服务注册了。
// NacosServiceRegistry.java
@Override
public void register(ServiceInfo serviceInfo) throws Exception {
// 注册当前服务实例
naming.registerInstance(serviceInfo.getServiceName(), buildInstance(serviceInfo));
}
private Instance buildInstance(ServiceInfo serviceInfo) {
// 将实例信息注册到 Nacos 中心
Instance instance = new Instance();
instance.setIp(serviceInfo.getIp());
instance.setPort(serviceInfo.getPort());
// TODO add more metadata
return instance;
}注意:NamingService 类提供了很多有用的方法,大家可自行进行尝试。
客户端实现服务发现
定义服务发现接口
前面已经将服务实例注册到Zookeeper 或者 Nacos 服务端,现在客户端想要调用服务端首先得获得服务端实例列表,这个过程其实就是服务发现。
我们先定义一个抽象的接口,这个接口主要的功能就是定义一个获取服务实例的接口:
public interface ServiceDiscovery {
/**
* 通过服务名称随机选择一个健康的实例
* @param serviceName 服务名称
* @return 实例对象
*/
InstanceInfo selectOneInstance(String serviceName);
}随机挑选一个实例是为了模拟负载均衡,尽量使请求均匀分配到各实例上。
Zookeeper 实现服务发现
前面使用 Zookeeper 实现了服务注册功能,这里我们再用 Zookeeper 来实现服务发现功能,先定义一个类实现 ServiceDiscovery 接口:
public class ZookeeperServiceDiscovery implements ServiceDiscovery {
}下面实现核心方法:selectOneInstance
Zookeeper 内部是一个树形的节点,通过查找一个指定节点的所有子节点即可获得服务实例列表。拿到服务实例列表后如何挑选出一个实例呢?这里就可以引入负载均衡算法了。
// ZookeeperServiceDiscovery.java
@Override
public InstanceInfo selectOneInstance(String serviceName) {
String servicePath = "/com/leixiaoshuai/easyrpc/" + serviceName + "/service";
final List<String> childrenNodes = zkClient.getChildren(servicePath);
return Optional.ofNullable(childrenNodes)
.orElse(new ArrayList<>())
.stream()
.map(node -> {
try {
// 将服务信息经过 URL 解码后反序列化为对象
String serviceInstanceJson = URLDecoder.decode(node, "UTF-8");
return JSON.parseObject(serviceInstanceJson, InstanceInfo.class);
} catch (UnsupportedEncodingException e) {
logger.error("Fail to decode", e);
}
return null;
}).filter(Objects::nonNull).findAny().get();
}注意:当前项目仅仅用于学习用,这里没有引入复杂的负载均衡算法,有兴趣的同学可自行补充,欢迎提交 MR 贡献代码。
Nacos 实现服务发现
最后来到 Nacos 的实现,话不多说先定义一个类:
public class NacosServiceDiscovery implements ServiceDiscovery {
}同样也需要实现核心方法:selectOneInstance。Nacos 的实现就比较简单了,因为 Nacos 官方提供的 SDK 功能太强大了,我们直接调用对应的接口就可以了,Nacos 根据算法会随机挑选一个健康的实例,我们不用关注细节。
// ZookeeperServiceDiscovery.java
@Override
public InstanceInfo selectOneInstance(String serviceName) {
Instance instance;
try {
// 调用 nacos 提供的接口,随机挑选一个服务实例,负载均衡的算法依赖 nacos 的实现
instance = namingService.selectOneHealthyInstance(serviceName);
} catch (NacosException e) {
logger.error("Nacos exception", e);
return null;
}
// 封装实例对象返回
InstanceInfo instanceInfo = new InstanceInfo();
instanceInfo.setServiceName(instance.getServiceName());
instanceInfo.setIp(instance.getIp());
instanceInfo.setPort(instance.getPort());
return instanceInfo;
}源码清单
服务注册和发现所用到的源码清单如下:
├── easy-rpc-example
├── easy-rpc-spring-boot-starter
│ ├── pom.xml
│ ├── src
│ │ └── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── leixiaoshuai
│ │ │ └── easyrpc
│ │ │ ├── client
│ │ │ │ ├── ClientProxyFactory.java
│ │ │ │ ├── discovery
│ │ │ │ │ ├── NacosServiceDiscovery.java
│ │ │ │ │ ├── ServiceDiscovery.java
│ │ │ │ │ └── ZookeeperServiceDiscovery.java
│ │ │ ├── common
│ │ │ │ └── InstanceInfo.java
│ │ │ └── server
│ │ │ └── registry
│ │ │ ├── NacosServiceRegistry.java
│ │ │ ├── ServiceRegistry.java
│ │ │ └── ZookeeperServiceRegistry.java完整的源码可以自行去 Github 上取:
https://github.com/CoderLeixiaoshuai/easy-rpc
小结
本文以较少的代码实现了 RPC 框架实现服务注册发现功能,相信大家对这个流程已经全面掌握了。
客户端与服务端通信的前提是需要知道对方的 ip 和端口,服务注册就是将自己的元信息(ip、端口等)注册到注册中心(Registry),这样客户端就可以从注册中心(Registry)获取自己"感兴趣"的服务实例了。
服务注册和发现机制可以通过一些中间件来辅助实现,如比较流行的:Zookeeper或者 Nacos 等。
推荐

PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。点“在看”支持我们吧!
边栏推荐
- TCP congestion control details | 2 background
- OpenHarmony如何启动远程设备的FA
- 大厂面试总结大全
- Global and Chinese markets for disposable insulin pumps 2022-2028: Research Report on technology, participants, trends, market size and share
- Leetcode1380: lucky numbers in matrix
- 国内比较好的OJ平台[通俗易懂]
- What is the difference between JSP and servlet?
- Analysis of how to prevent virus in industrial computer
- 【Leetcode】14. 最长公共前缀
- Learning Weekly - total issue 60 - 25th week of 2022
猜你喜欢

unity Hub 登錄框變得很窄 無法登錄
![L'explosion de John utilise l'encodage d'entrée par défaut: UTF - 8 Loaded 1 password Hash (bcrypt [blowfish 32 / 64 X3])](/img/4c/ddf7f8085257d0eb8766dbec251345.png)
L'explosion de John utilise l'encodage d'entrée par défaut: UTF - 8 Loaded 1 password Hash (bcrypt [blowfish 32 / 64 X3])

How to solve the failure of printer driver installation of computer equipment

Cell: Tsinghua Chenggong group revealed an odor of skin flora. Volatiles promote flavivirus to infect the host and attract mosquitoes

Executive engine module of high performance data warehouse practice based on Impala

Where can I open computer administrator permissions

亚马逊云科技 Community Builder 申请窗口开启

Download blender on Alibaba cloud image station

配置基于接口的ARP表项限制和端口安全(限制用户私自接入傻瓜交换机或非法主机接入)

TCP server communication process (important)
随机推荐
[error record] error -32000 received from application: there are no running service protocol
Xiaopeng P7 had an accident on rainy days, and the airbag did not pop up. Official response: the impact strength did not meet the ejection requirements
Take you ten days to easily complete the go micro service series (I)
Notice on holding a salon for young editors of scientific and Technological Journals -- the abilities and promotion strategies that young editors should have in the new era
易语言abcd排序
La boîte de connexion du hub de l'unit é devient trop étroite pour se connecter
LeetCode 2. 两数相加
LeetCode 1. 两数之和
What is generics- Introduction to generics
Cell: Tsinghua Chenggong group revealed an odor of skin flora. Volatiles promote flavivirus to infect the host and attract mosquitoes
LeetCode 1. Sum of two numbers
OpenHarmony如何启动远程设备的FA
2322. 从树中删除边的最小分数(异或和&模拟)
AcWing 300. Task arrangement
VMware安装win10镜像
Download blender on Alibaba cloud image station
流批一体在京东的探索与实践
Yyds dry goods inventory # look up at the sky | talk about the way and principle of capturing packets on the mobile terminal and how to prevent mitm
Global and Chinese market of switching valves 2022-2028: Research Report on technology, participants, trends, market size and share
大厂面试总结大全