1.前言
本文章是笔主在声哥的手写RPC框架的学习下,对注册中心的一个拓展。因为声哥某些部分没有保留拓展性,所以本文章的项目与声哥的工程有部分区别,核心内容在Curator的注册发现与注销,思想看准即可。
本文章Git仓库:zko0/zko0-rpc
声哥的RPC项目写的确实很详细,跟学一遍受益匪浅:
在声哥的项目里使用Nacos作为了服务注册中心。本人拓展添加了ZooKeeper实现服务注册。
Nacos的服务注册和发现,设计的不是非常好,每次服务的发现都需要去注册中心拉取。本人实现ZooKeeper注册中心时,参考了Dubbo的设计原理,结合本人自身想法,添加了本地缓存:
- Client发现服务后缓存在本地,维护一个服务——实例列表
- 当监听到注册中心的服务列表发生了变化,Client更新本地列表
- 当注册中心宕机,Client能够依靠本地的服务列表继续提供服务
问题:
- 实现服务注册的本地缓存,还需要实现注册中心的监听,当注册中心的服务发生更改时能够实现动态更新。或者用轮训的方式,定时更新,不过这种方式的服务实时性较差
- 当Server宕机,非临时节点注册容易出现服务残留无法清除的问题。所以我建议全部使用临时节点去注册。
2.内容
zookeeper需要简单学一下,知识内容非常简单,搭建也很简单,在此跳过。
如果你感兴趣,可以参考我的ZooKeeper的文章:Zookeeper学习笔记 - zko0
①添加依赖
Curator:(简化ZooKeeper客户端使用)(Netfix研发,捐给Apache,是Apache顶级项目)
这里排除slf4j依赖,因为笔主使用的slf4j存在冲突
<!-- https://mvnrepository.com/artifact/org.apache.curator/curator-recipes -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.2.0</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
②代码编写
1.首先创建一个连接类:
@Slf4j
public class ZookeeperUtil {
//内部化构造方法
private ZookeeperUtil(){
}
private static final String SERVER_HOSTNAME= RegisterCenterConfig.getHostName();
private static final Integer SERVER_PORT=RegisterCenterConfig.getServerPort();
private static CuratorFramework zookeeperClient;
public static CuratorFramework getZookeeperClient(){
if (zookeeperClient==null){
synchronized (ZookeeperUtil.class){
if (zookeeperClient==null){
RetryPolicy retryPolic=new ExponentialBackoffRetry(3000,10);
zookeeperClient = CuratorFrameworkFactory.builder()
.connectString(SERVER_HOSTNAME+":"+SERVER_PORT)
.retryPolicy(retryPolic)
// zookeeper根目录为/serviceRegister,不为/
.namespace("serviceRegister")
.build();
zookeeperClient.start();
}
}
}
return zookeeperClient;
}
public static String getServerHostname(){
return SERVER_HOSTNAME;
}
public static Integer getServerPort(){
return SERVER_PORT;
}
}
其中HOST,PORT信息我保存在regiserCenter.properties配置文件夹中,使用类读取:
public class RpcConfig {
//注册中心类型
private static String registerCenterType;
//序列化类型
private static String serializerType;
//负载均衡类型
private static String loadBalanceType;
//配置Nacos地址
private static String registerCenterHost;
private static Integer registerCenterPort;
private static boolean zookeeperDestoryIsEphemeral;
private static String serverHostName;
private static Integer serverPort;
static {
ResourceBundle bundle = ResourceBundle.getBundle("rpc");
registerCenterType=bundle.getString("registerCenter.type");
loadBalanceType=bundle.getString("loadBalance.type");
registerCenterHost=bundle.getString("registerCenter.host");
registerCenterPort = Integer.parseInt(bundle.getString("registerCenter.port"));
try {
zookeeperDestoryIsEphemeral="true".equals(bundle.getString("registerCenter.destory.isEphemeral"));
} catch (Exception e) {
zookeeperDestoryIsEphemer