【BlossomRPC】接入注册中心

文章目录

  • Nacos
  • Zookeeper
  • 自研配置中心

RPC项目

配置中心项目

网关项目

这是BlossomRPC项目的最后一篇文章了,接入完毕注册中心,一个完整的RPC框架就设计完成了。
对于项目对注册中心的整合,其实我们只需要再服务启动的时候将ip/port/servicename注册到注册中心上即可。
注册中心这里只是一个简单的ip/port信息的存储器而已。
那么我们就需要考虑用什么样的一种方式来引入注册中心。
这里可以考虑使用Spring的AutoConfiguration然后配合Conditional类型的注解进行判断使用那个注册中心。
这里我提供了一个RegisterService接口提供抽象的注册方法,只需要实现这个接口就可以再项目中引入自己实现的注册中心了。

public interface RegisterService {default void init(){}void register(RpcServiceInstance instance);default void unregister(RpcServiceInstance instance){}RpcServiceInstance discovery(RpcServiceInstance instance);
}

Nacos

我们首先以Nacos为例。实现所有的接口方法。

package blossom.project.rpc.nacos;import blossom.project.rpc.common.register.RegisterService;
import blossom.project.rpc.common.register.RpcServiceInstance;
import blossom.project.rpc.common.loadbalance.LoadBalanceStrategy;
import blossom.project.rpc.common.loadbalance.PollLoadBalance;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingFactory;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import lombok.extern.slf4j.Slf4j;import java.util.List;
import java.util.Objects;/*** @author: ZhangBlossom* @date: 2023/12/19 23:46* @contact: QQ:4602197553* @contact: WX:qczjhczs0114* @blog: https://blog.csdn.net/Zhangsama1* @github: https://github.com/ZhangBlossom* NacosRegisterService类*/
@Slf4j
public class NacosRegisterService implements RegisterService {private NamingService namingService;private LoadBalanceStrategy<Instance> loadBalanceStrategy= new PollLoadBalance<>();public NacosRegisterService(){}public NacosRegisterService(String serverAddress) {try {this.namingService = NamingFactory.createNamingService(serverAddress);} catch (NacosException e) {throw new RuntimeException(e);}}public NacosRegisterService(String serverAddress, LoadBalanceStrategy loadBalanceStrategy) {try {this.namingService = NamingFactory.createNamingService(serverAddress);this.loadBalanceStrategy = loadBalanceStrategy;} catch (NacosException e) {throw new RuntimeException(e);}}@Overridepublic void register(RpcServiceInstance instance) {if (Objects.isNull(instance)) {log.info("the Reigster Service Info can not be null!!!");return;}log.info("start to register instance to Nacos: {}",instance);try {//注册服务  服务名称:blossom.project.rpc.core.service.RpcServicenamingService.registerInstance(instance.getServiceName(), instance.getServiceIp(),instance.getServicePort());} catch (NacosException e) {log.error("register the ServiceInstance to Nacos failed!!!");throw new RuntimeException(e);}}@Overridepublic void unregister(RpcServiceInstance instance) {if (Objects.isNull(instance)) {log.info("the Reigster Service Info can not be null!!!");return;}log.info("start to unregister instance to Nacos: {}",instance);try {//进行服务注销namingService.deregisterInstance(instance.getServiceName(), instance.getServiceIp(),instance.getServicePort());} catch (NacosException e) {log.error("unregister the ServiceInstance from Nacos failed!!!");throw new RuntimeException(e);}}@Overridepublic RpcServiceInstance discovery(RpcServiceInstance instance) {try {List<Instance> instances = namingService.selectInstances(instance.getServiceName(),instance.getGroupName(), true);Instance rpcInstance = loadBalanceStrategy.choose(instances);if (Objects.nonNull(rpcInstance)) {return RpcServiceInstance.builder().serviceIp(rpcInstance.getIp()).servicePort(rpcInstance.getPort()).serviceName(rpcInstance.getServiceName()).build();}return null;} catch (NacosException e) {log.error("discovery the ServiceInstance from Nacos failed!!!");throw new RuntimeException(e);}}}

之后,我们得考虑,如何让用户无感知的只需要启动项目和引入依赖,就可以完成服务的注册。
我们考虑使用@AutoConfiguration的方式来进行。
同时,还得预留一种情况,就是用于自研的注册中心。

package blossom.project.rpc.nacos;import blossom.project.rpc.common.constants.RpcCommonConstants;
import blossom.project.rpc.common.loadbalance.LoadBalanceStrategy;
import blossom.project.rpc.common.loadbalance.PollLoadBalance;
import blossom.project.rpc.common.register.RegisterService;
import org.springframework.beans.BeansException;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;/*** @author: ZhangBlossom* @date: 2023/12/22 18:50* @contact: QQ:4602197553* @contact: WX:qczjhczs0114* @blog: https://blog.csdn.net/Zhangsama1* @github: https://github.com/ZhangBlossom* NacosAutoConfiguration类*/
@Configuration
//@AutoConfiguration
//@AutoConfigureBefore(value = RegisterService.class)
@AutoConfigureOrder(value = Integer.MAX_VALUE)
//@Conditional(OnNacosClientClassCondition.class)
public class NacosAutoConfiguration implementsApplicationContextAware, EnvironmentAware {/*** 这个bean只会在存在nacos的依赖的时候才会创建** @return*/@Primary@Bean(name = "nacosRegisterService")@ConditionalOnMissingBean(name = "spiRegisterService")public RegisterService nacosRegisterService() {//创建注册中心// 优先检查是否存在 SPI 实现类// 获取Nacos相关配置,例如服务器地址等//String serverAddress = "localhost:8848"; // 从配置中读取Nacos服务器地址// ... 其他所需配置String registerAddress = environment.getProperty(RpcCommonConstants.REGISTER_ADDRESS);try {// 使用反射创建NamingService实例//Class<?> namingFactoryClass =//        Class.forName("com.alibaba.nacos.api.naming.NamingFactory");//Method createNamingServiceMethod =//        namingFactoryClass.getMethod("createNamingService", String.class);//Object namingServiceInstance = createNamingServiceMethod.invoke(null, serverAddress);// 创建NacosRegisterService实例Class<?> nacosRegisterServiceClass = Class.forName(RpcCommonConstants.NACOS_REGISTER_CLASS);Constructor<?> constructor = nacosRegisterServiceClass.getConstructor(String.class,LoadBalanceStrategy.class);return (RegisterService) constructor.newInstance(registerAddress, new PollLoadBalance<>());} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException |InvocationTargetException e) {throw new IllegalStateException("Failed to create NacosRegisterService", e);}}private ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}private Environment environment;@Overridepublic void setEnvironment(Environment environment) {this.environment = environment;}
}

通过这种方式,只要引入了Nacos的依赖,在项目启动的时候就会使用Nacos作为项目的注册中心。
同时,如果用户也提供了自己的注册中心,那么会优先使用用户自己的注册中心来进行服务注册。
而用户自己的注册中心的实现,使用的是SPI的方式。

package blossom.project.rpc.core.proxy.spring;import blossom.project.rpc.common.constants.RpcCommonConstants;
import blossom.project.rpc.common.loadbalance.LoadBalanceStrategy;
import blossom.project.rpc.common.loadbalance.PollLoadBalance;
import blossom.project.rpc.common.register.RegisterService;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;import java.util.Map;
import java.util.ServiceLoader;@Configuration
public class SpringRegisterServicePostProcessor implementsBeanDefinitionRegistryPostProcessor ,EnvironmentAware,ApplicationContextAware {private Environment environment;@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {ServiceLoader<RegisterService> serviceLoader = ServiceLoader.load(RegisterService.class);if (!serviceLoader.iterator().hasNext()) {// 没有通过SPI找到实现,加载Nacos或Zookeeper的实现String registerAddress = environment.getProperty(RpcCommonConstants.REGISTER_ADDRESS);registerServiceBeanDefinition(registry, registerAddress);} else {// 通过SPI找到了实现,将其注册到Spring容器registerServiceViaSpi(serviceLoader, registry);}}private void registerServiceViaSpi(ServiceLoader<RegisterService> serviceLoader, BeanDefinitionRegistry registry) {// 获取SPI的RegisterService实现RegisterService registerService = serviceLoader.iterator().next();// 创建BeanDefinitionBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(registerService.getClass()).getBeanDefinition();// 注册BeanDefinition到Spring容器registry.registerBeanDefinition("spiRegisterService", beanDefinition);}private void registerServiceBeanDefinition(BeanDefinitionRegistry registry, String registerAddress) {try {registerReflectiveService(registry, RpcCommonConstants.NACOS_REGISTER_CLASS, registerAddress);} catch (Exception e) {registerReflectiveService(registry, RpcCommonConstants.ZK_REGISTER_CLASS, registerAddress);}}private void registerReflectiveService(BeanDefinitionRegistry registry, String className, String registerAddress) {try {Class<?> registerServiceClass = Class.forName(className);BeanDefinition beanDefinition =BeanDefinitionBuilder.genericBeanDefinition(registerServiceClass).getBeanDefinition();registry.registerBeanDefinition(className, beanDefinition);System.out.println(registry.getBeanDefinition(className));} catch (Exception e) {throw new RuntimeException("Failed to register " + className, e);}}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {// No implementation required for this method in this context}@Overridepublic void setEnvironment(Environment environment) {this.environment = environment;}private ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}}

好的,这里我们已Nacos为例,对服务进行启动。
成功完成服务实例的注册
在这里插入图片描述启动多个服务实例也可以
在这里插入图片描述
同理,对于zk,也是一样的方法。

Zookeeper

@Slf4j
public class ZookeeperRegisterService implements RegisterService {private static final String REGISTRY_PATH = "/rpc_registry";/*** zk注册中心*/private final ServiceDiscovery<RpcServiceInstance> serviceDiscovery;private LoadBalanceStrategy<ServiceInstance<RpcServiceInstance>> loadBalanceStrategy;public ZookeeperRegisterService(String serverAddress,LoadBalanceStrategy loadBalanceStrategy) throws Exception {CuratorFramework client = CuratorFrameworkFactory.newClient(serverAddress,new ExponentialBackoffRetry(2000, 3));client.start();JsonInstanceSerializer<RpcServiceInstance> serializer = new JsonInstanceSerializer<>(RpcServiceInstance.class);this.serviceDiscovery =ServiceDiscoveryBuilder.builder(RpcServiceInstance.class).client(client).serializer(serializer).basePath(REGISTRY_PATH).build();this.serviceDiscovery.start();this.loadBalanceStrategy = loadBalanceStrategy;}@Overridepublic void register(RpcServiceInstance instance) {if (Objects.isNull(instance)) {log.info("the Reigster Service Info can not be null!!!");return;}log.info("start to register instance to Zookeeper: {}",instance);try {ServiceInstance<RpcServiceInstance> serviceInstance =ServiceInstance.<RpcServiceInstance>builder().name(instance.getServiceName()).address(instance.getServiceIp()).port(instance.getServicePort()).payload(instance).build();this.serviceDiscovery.registerService(serviceInstance);} catch (Exception e) {throw new RuntimeException(e);}}@Overridepublic RpcServiceInstance discovery(RpcServiceInstance instance) {Collection<ServiceInstance<RpcServiceInstance>> serviceInstances = null;try {serviceInstances = this.serviceDiscovery.queryForInstances(instance.getServiceName());ServiceInstance<RpcServiceInstance> serviceInstance =this.loadBalanceStrategy.choose((List<ServiceInstance<RpcServiceInstance>>) serviceInstances);if (serviceInstance != null) {return serviceInstance.getPayload();}return null;} catch (Exception e) {throw new RuntimeException(e);}}}public class OnZookeeperClientClassCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {try {Class.forName(ZK_DISCOVERY_CLASS);return true;} catch (ClassNotFoundException e) {return false;}}

自研配置中心

上文提到,有可能用户会使用自己的注册中心。所以我提供了基于spi机制的方式,来让用户引入自己的注册中心。
在这里插入图片描述
用户在项目启动的时候通过SPI的方式提供自己实现的注册中心代码即可。
如果不存在会扫描是否存在Nacos/Zk,如果都不存在,就报错,否则优先使用用户自定义的配置中心。

到此为止,一个简单的自研配置中心就完成了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/785985.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt6.6添加多媒体模块Multimedia报错问题

问题 QT包含多媒体模块Multimedia时提示未知的模块&#xff1a; error: Project ERROR: Unknown module(s) in QT: multimedia 在帮助文档中只可以找到QMediaPlayer类&#xff0c;但是点进去是空的&#xff0c;这是因为没有安装多媒体模块及对应的帮助文档。 解决 使用在线…

● 435. 无重叠区间 ● 763.划分字母区间 ● 56. 合并区间

● 435. 无重叠区间 class Solution:def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:if len(intervals)1:return 0intervalssorted(intervals,keylambda x:(x[0],x[1]))res0for i in range(1,len(intervals)):if intervals[i][0]<intervals[i-1][…

代码随想录算法训练营第41天|343. 整数拆分 |96.不同的二叉搜索树

代码随想录算法训练营第41天|343. 整数拆分 |96.不同的二叉搜索树 详细布置 今天两题都挺有难度&#xff0c;建议大家思考一下没思路&#xff0c;直接看题解&#xff0c;第一次做&#xff0c;硬想很难想出来。 343. 整数拆分 https://programmercarl.com/0343.%E6%95%B4%E6%…

【Go】十三、面向对象:方法

文章目录 1、面向对象2、结构体实例的创建3、结构体之间的转换4、方法5、结构体值拷贝6、方法的注意点7、方法和函数的区别8、跨包创建结构体实例 1、面向对象 Go的结构体struct ⇒ Java的Class类Go基于struct来实现OOP相比Java&#xff0c;Go去掉了方法重载、构造函数和析构函…

css- 4

1.浮动 1. 浮动最初用于实现文字环绕效果 2. 现在&#xff0c;浮动是主流的布局方式之一 1.1元素浮动之后的特点 元素浮动之后&#xff0c;称为浮动元素&#xff0c;具有如下特点&#xff1a; 1. 浮动元素脱离文档流 2. 多个浮动的元素会水平排列&#xff0c;一行放不下自动换…

Redis高级面试题-2024

说说你对Redis的理解 Redis是一个基于Key-Value存储结构的开源内存数据库&#xff0c;也是一种NoSQL数据库。 它支持多种数据类型&#xff0c;包括String、Map、Set、ZSet和List&#xff0c;以满足不同应用场景的需求。 Redis以内存存储和优化的数据结构为基础&#xff0c;提…

chatglm.cpp编译与执行

ChatGLM3介绍 ChatGLM3是由智谱AI和清华大学KEG实验室联合发布的对话预训练模型。作为第三代大型语言模型&#xff0c;ChatGLM3不仅理解和生成人类语言&#xff0c;还能执行代码、调用工具&#xff0c;并以markdown格式进行响应。其目标是打造更智能、更安全的代码解释器和工具…

【力扣一刷】代码随想录day27(39. 组合总和、40.组合总和II、131.分割回文串)

目录 【39. 组合总和】中等题 【40.组合总和II】中等题 【131. 分割回文串】中等题 【39. 组合总和】中等题 思路&#xff1a; 确定终止条件&#xff1a;sum target时记录路径并返回。剪枝&#xff1a;当前节点的路径之和已经大于sum就不可能再等于sum了&#xff0c;结束该分支…

16进制的字符串转byte[]数组 以及将字节数组转换成十六进制的字符串

16进制的字符串转byte[]数组 public class ClientString16 {@Testpublic void get16Str(){String str="48 47 12 00 14 12 16 08 15 0d 30 0f 02 30 30 30 30 30 30 30 30 30 30 00 c2";byte[] bytes = hexStringToByteArray(str);getBytetoString(bytes);//String …

【御控物联】JavaScript JSON结构转换(12):对象To数组——键值互换属性重组

文章目录 一、JSON结构转换是什么&#xff1f;二、核心构件之转换映射三、案例之《JSON对象 To JSON数组》四、代码实现五、在线转换工具六、技术资料 一、JSON结构转换是什么&#xff1f; JSON结构转换指的是将一个JSON对象或JSON数组按照一定规则进行重组、筛选、映射或转换…

【御控物联】JavaScript JSON结构转换(8):数组To数组——多层属性重组

文章目录 一、JSON结构转换是什么&#xff1f;二、案例之《JSON数组 To JSON数组》三、代码实现四、在线转换工具五、技术资料 一、JSON结构转换是什么&#xff1f; JSON结构转换指的是将一个JSON对象或JSON数组按照一定规则进行重组、筛选、映射或转换&#xff0c;生成新的JS…

Java

1.学生和老师都会有work方法&#xff0c;学生的工作是学习&#xff0c;老师的工作是教书&#xff0c;我利用了一个接口来实现&#xff1b; 2.同时&#xff0c;老师和学生都是人&#xff0c;并且都有姓名&#xff0c;姓名&#xff0c;年龄和身高等特征&#xff0c;我用了一个继承…

http协议补充

7.7实现http请求 class httprequest { public:void Deserialize(std::string content){while (true){auto pos content.find(sep);if (pos std::string::npos){break;}std::string temp content.substr(0, pos);if (temp.empty()){break;}req_header_.push_back(temp);cont…

轮播卡片切换实现

背景 如上图&#xff0c;实现一个轮播卡片能力&#xff0c;支持手动滑动&#xff0c;手动拖动&#xff0c;已经点击两端切换按钮。 实现 <template><div class"top-menu"><!-- Swiper --><div v-if"headerMenuList.length > 9" c…

并查集----格子游戏

并查集中最重要的是要搞懂&#xff1a; 不明白的可以拿纸自己先演示一番&#xff0c;find函数不仅能找到他们的祖先数&#xff0c;而且同时也能更新路径的子结点都等于祖先&#xff0c;然后以后寻找时会更加的方便&#xff01;

蓝桥杯单片机速成4-温度传感器DS18B20

目录 一、电路图 二、底层驱动代码 三、温度读取实现 四、实际使用 一、电路图 二、底层驱动代码 时序是单总线 我们需要修改的地方是单总线内部延时函数&#xff0c;改成 void Delay_OneWire(unsigned int t) { t*12; while(t--); } #ifndef __ONEWIRE_H #defi…

Unity TrailRenderer的基本了解

在Unity中&#xff0c;TrailRenderer组件用于在对象移动时创建轨迹效果。通常用于增强游戏中的动态物体&#xff0c;比如子弹、飞行道具或者角色移动时的拖尾效果。 下面来了解下它的基本信息。 1、创建 法1&#xff1a;通过代码创建 using UnityEngine;public class Trail…

动态规划刷题(算法竞赛、蓝桥杯)--合唱队形(线性DP)

1、题目链接&#xff1a;[NOIP2004 提高组] 合唱队形 - 洛谷 #include <bits/stdc.h> using namespace std; int n,ans; int a[105],f[105][2];//f[i][2]中2表示正反两个方向int main(){cin>>n;for(int i1;i<n;i){cin>>a[i];}//正方向求最长上升子序列 a[…

[flink 实时流基础] 输出算子(Sink)

学习笔记 Flink作为数据处理框架&#xff0c;最终还是要把计算处理的结果写入外部存储&#xff0c;为外部应用提供支持。 文章目录 **连接到外部系统****输出到文件**输出到 Kafka输出到 mysql自定义 sink 连接到外部系统 Flink的DataStream API专门提供了向外部写入数据的方…