【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.rhkb.cn/news/294730.html

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

相关文章

区块链食品溯源案例实现(二)

引言 随着前端界面的完成&#xff0c;我们接下来需要编写后端代码来与区块链网络进行交互。后端将负责处理前端发送的请求&#xff0c;调用智能合约的方法获取食品溯源信息&#xff0c;并将结果返回给前端。 通过前后端的整合&#xff0c;我们可以构建一个食品溯源系统&#xf…

spring boot3登录开发-3(2短信验证登录/注册逻辑实现)

⛰️个人主页: 蒾酒 &#x1f525;系列专栏&#xff1a;《spring boot实战》 &#x1f30a;山高路远&#xff0c;行路漫漫&#xff0c;终有归途 目录 写在前面 上文衔接 内容简介 功能分析 短信验证登录实现 1.创建交互对象 用户短信登录/注册DTO 创建用户登录VO…

Linux(CentOS)安装Redis教程_简单快捷

一、安装依赖 因为redis是用C语言开发的&#xff0c;所以在安装之前需要确定是否安装gcc环境&#xff08;gcc -v&#xff09;&#xff0c;如果没有安转可以执行一下命令进行安装 [rootlocalhost ~]# yum install -y gcc 二、下载安装包 1.在官网先进行下载 官网地址&#x…

【接口测试】Postman(一)--接口测试知识准备

1.0 前言 ​ 应用程序编程接口&#xff08;Application Programming Interface, API&#xff09;是这些年来最流行的技术之一&#xff0c;强大的Web应用程序和领先的移动应用程序都离不开后端强大的API。API技术的应用给系统开发带来了便利&#xff0c;但也对测试人员提出了更…

Ubuntu joystick 测试手柄 xbox

Ubuntu joystick 测试手柄 xbox 测试使用Ubuntu20.04 测试环境在工控机 安装测试 实际测试使用的手柄是北通阿修罗2pro 兼容xbox Ubuntu20.04主机 连接手柄或者无线接收器后查看是否已经检测到&#xff1a; ls /dev/input找到输入中的 js0 即为手柄输入 需要安装joysti…

浅谈 kafka

引言 同事在公司内部分享了关于 kafka 技术一些相关的内容&#xff0c;所以有了这篇文章&#xff1b;部分图片选自网络摘抄&#xff1b; 1 Kafka概述 1.1 定义 Kafka传统定义&#xff1a;kafka是一个分布式的基于发布/订阅模式的消息队列。 Kafka最新定义&#xff1a;kafka…

【讲解下Gitea】

&#x1f308;个人主页:程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

突破编程_前端_JS编程实例(分割窗体组件)

1 开发目标 分隔窗体组件旨在提供灵活的窗体分隔功能&#xff0c;支持横向分割与纵向分隔两种类型&#xff0c;并具备拖拽调整窗体比例的功能&#xff0c;同时提供最小比例设置&#xff0c;以防止窗体被过度缩小&#xff1a; 2 详细需求 2.1 分隔窗体类型 &#xff08;1&…

AI改写文案的注意事项

AI改写文案的注意事项 随着人工智能技术的不断发展&#xff0c;AI改写文案成为了一种新兴的应用场景。通过AI改写文案&#xff0c;可以快速生成大量内容&#xff0c;节省时间和人力成本&#xff0c;但在实际应用中也需要注意一些问题和注意事项。 1. 确保内容原创性 尽管AI改…

个人做量化交易是否可行呢?

考虑个人做量化交易&#xff0c;需要完成两步&#xff1a; 解决3个“什么”的问题&#xff1a;“你要在什么时间&#xff1f;交易什么标的&#xff1f;交易数量是多少&#xff1f;”把你的想法准确地表达出来&#xff0c;告诉交易下单系统 也就是自己形成策略----自己去实现。…

文件操作详解

1.为什么使用文件 目录 1.为什么使用文件 2.什么是文件 2.1程序文件 2.2数据文件 2.3文件名字 3.二进制文件与文本文件 4.文件的打开和关闭 4.1 流 4.2标准流 4.3文件指针 4.4 文件的打开和关闭 fopen fclose 5.文件的顺序读写 5.1文件读取结束原因的判定 5…

mysql 本地电脑服务部署

前提&#xff1a; 下载mysql 新建配置文档 在安装mysql目录新建 my.ini [mysqld] # 设置3306端口 port3306#设置mysql的安装目录 basedirC:\Program Files\MySQL\MySQL Server 8.3 #切记此处一定要用双斜杠\\,单斜杠我这里会出错&#xff0c;不过看别人的教程&#xff0c;有…

走进车厂 | 移远通信以前沿车载技术,照亮智能网联汽车产业创新发展之路

无钥匙自动解锁方便快捷、实时路况导航精准高效、语音指令轻松控制车辆、车载娱乐系统丰富多样……随着智能化、数字化浪潮的不断推进&#xff0c;现如今的汽车出行焕然一新。 正如我们所见&#xff0c;汽车产业正在经历前所未有的变革。物联网、车联网等前沿技术的发展和应用&…

argocd部署

一、前言 ArgoCD 是一个开源的、持续交付工具&#xff0c;用于自动化部署应用程序到 Kubernetes 集群。它基于 GitOps 理念&#xff0c;通过使用 Git 作为单一的源头来管理应用程序的配置和部署状态&#xff0c;argocd会定时监控git仓库中的yaml配置文件&#xff0c;当git仓库中…

后端前行Vue之路(二):模版语法之插值与指令

1.概述 Vue.js的模板语法是一种将Vue实例的数据绑定到HTML文档的方法。Vue的模板语法是一种基于HTML的扩展&#xff0c;允许开发者将Vue实例中的数据绑定到HTML元素&#xff0c;以及在HTML中使用一些简单的逻辑和指令。Vue.js 基于 HTML 的模板语法允许开发者声明式地将 DOM 绑…

瑞麦德机电设备将莅临2024第13届生物发酵展

参展企业介绍 河南瑞麦德机电设备有限公司是专业从事机械输送气力输送、称重配料、筛分、磁选设备研发和制造于一体的企业&#xff0c;公司采用国内外同行业产品的先进技术&#xff0c;经专业团队设计、研发、生产&#xff0c;产品满足“ISO9001”&#xff0c;“GMP”等标准要…

超图新建三维数据集继续学习

1 新建三维数据集 之前操作过新建三维数据集&#xff0c;还不熟悉&#xff0c;继续熟悉&#xff1b; 现在有一个文件型的数据源&#xff0c;名为swtest1&#xff1b;它前面小图标上有UDX三个字母&#xff0c;表明这是一个UDX类型的数据源&#xff1b;在此数据源上右击&#x…

LEAP模型的能源环境发展、碳排放建模预测及不确定性分析教程

原文链接&#xff1a;LEAP模型的能源环境发展、碳排放建模预测及不确定性分析教程https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247599754&idx4&sn243c9f8bff355235a7056c2cbb1331fa&chksmfa82076dcdf58e7b871c3369c95ead9ff1d90baa0431318b26b6abd27…

剑指offer--数组中重复的数字

一.题目描述 在一个长度为 n 的数组 nums 里的所有数字都在 0&#xff5e;n-1 的范围内。数组中某些数字是重复的&#xff0c;但不知道有几个数字重复了&#xff0c;也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。 算法1.排序,然后遍历,时间复杂度O(nlogn),空…

Flask Python:请求上下文和应用上下文

请求上下文和应用上下文详解 一、背景二、什么是上下文2.1、请求上下文2.2、应用上下文2.3、两种上下文的底层逻辑 三、写在最后 一、背景 在如何实现异步发送邮件的时候&#xff0c;遇到过这样一个报错 RuntimeError: Working outside of request context.This typically me…