SpringBoot通过配置切换注册中心(多注册中心nacos和eureka)

场景:

因项目需要,一个springcloud微服务工程需要同时部署到A,B两个项目使用,但A项目使用Eureka注册中心,B项目使用Nacos注册中心,现在需要通过部署时修改配置来实现多注册中心的切换。

解决思路:

如果同时引入nacos和eureka的依赖和配置,不做任何处理,会导致启动失败:

powershell 复制代码
***************************
APPLICATION FAILED TO START
***************************
 
Description:
 
Field registration in org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration$ServiceRegistryEndpointConfiguration required a single bean, but 2 were found:
	- nacosRegistration: defined by method 'nacosRegistration' in class path resource [com/alibaba/cloud/nacos/registry/NacosServiceRegistryAutoConfiguration.class]
	- eurekaRegistration: defined in BeanDefinition defined in class path resource [org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration$RefreshableEurekaClientConfiguration.class]
 
 
Action:
 
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

不难看出失败原因是单例bean找到了两个实例,那么该如何解决这个问题呢?首先想到的肯定是删除掉暂时不需要使用的实例(如使用eureka注册中心则删掉引入pom的nacos依赖),这样做是没有问题的,但是维护成本比较高。能不能从springboot自动装配原理入手,找到更便捷的方法呢?接着看:

我们都知道SpringBoot的启动类的@SpringBootApplication是一个组合注解,它里面的@EnableAutoConfiguration会引入AutoConfigurationImportSelector.class

从这个类的方法getAutoConfigurationEntry()一层一层点进去看,

SpringFactoriesLoader.loadFactories()会去检索META-INF/spring.factories文件。

powershell 复制代码
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
		return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
	}

那么思路就比较清晰了,我们可以通过实现AutoConfigurationImportFilter接口,将自己的过滤逻辑写在实现类中,就可以实现自定义的自动装配过滤器了。

上代码:

通过把1、2、3的代码放到一个starter中,然后在具体的项目中引用这个starter,配置文件中添加4的配置就可以切换了,当然具体nacos和eureka在yml中的配置还是分开写,只需指定用那个配置就行

1.过滤器

powershell 复制代码
package com.demo.business;
 
import com.demo.business.constants.RegistrationCenterConstants;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.AutoConfigurationImportFilter;
import org.springframework.boot.autoconfigure.AutoConfigurationMetadata;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
 
public class EngineAutoConfigurationImportFilter implements AutoConfigurationImportFilter, EnvironmentAware {
 
    private Environment environment;
 
    @Override
    public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
        //获取配置的注册中心,默认为nacos
        String registryType = environment.getProperty("registry.type", RegistrationCenterConstants.NACOS);
        boolean[] match = new boolean[autoConfigurationClasses.length];
        //当自定义标识为eureka,则排除nacos的自动装配,反之同理;
        if (registryType.equals(RegistrationCenterConstants.EUREKA)) {
            for (int i = 0; i < autoConfigurationClasses.length; i++) {
                match[i] = !StringUtils.isNotBlank(autoConfigurationClasses[i]) ||
                        !autoConfigurationClasses[i].equals(RegistrationCenterConstants.NACOS_SERVICE_REGISTRY_AUTO_CONFIGURATION);
            }
        } else {
            for (int i = 0; i < autoConfigurationClasses.length; i++) {
                if (StringUtils.isNotBlank(autoConfigurationClasses[i])){
                    match[i] = !RegistrationCenterConstants.EUREKA_DISCOVERY_CLIENT_CONFIGURATION.equals(autoConfigurationClasses[i])
                            && !RegistrationCenterConstants.EUREKA_AUTO_CONFIGURATION_CLASSES.equals(autoConfigurationClasses[i]);
                }
 
            }
        }
        return match;
    }
 
    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
}

或者以下这样也可以
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.AutoConfigurationImportFilter;
import org.springframework.boot.autoconfigure.AutoConfigurationMetadata;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;

public class EngineAutoConfigurationImportFilter implements AutoConfigurationImportFilter, EnvironmentAware {
    private Environment environment;

    public EngineAutoConfigurationImportFilter() {
    }

    public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
        String registryType = this.environment.getProperty("registry.type", "eureka");
        boolean[] match = new boolean[autoConfigurationClasses.length];
        //提取成常量
        String prefix = registryType.equals("nacos") ? "org.springframework.cloud.netflix.eureka" : "com.alibaba.cloud.nacos";

        for(int i = 0; i < autoConfigurationClasses.length; ++i) {
            if (StringUtils.isNotBlank(autoConfigurationClasses[i])) {
                match[i] = !autoConfigurationClasses[i].startsWith(prefix);
            }
        }

        return match;
    }

    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
}

2.常量类

powershell 复制代码
package com.demo.business.constants;
 
/**
 * 注册中心相关常量类
 */
public class RegistrationCenterConstants {
 
    public static final String NACOS = "nacos";
 
    public static final String EUREKA = "eureka";
 
    public static final String EUREKA_AUTO_CONFIGURATION_CLASSES = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration";
 
    public static final String EUREKA_DISCOVERY_CLIENT_CONFIGURATION = "org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration";
 
    public static final String NACOS_SERVICE_REGISTRY_AUTO_CONFIGURATION = "com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration";

或者
public static final String NACOS_PREFIX = "com.alibaba.cloud.nacos";
    public static final String EUREKA_PREFIX = "org.springframework.cloud.netflix.eureka";
 
}

3.spring.factories文件(注意路径一定要在META-INF包下)

powershell 复制代码
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
  com.example.demo.business.EngineAutoConfigurationImportFilter

4.配置文件添加

powershell 复制代码
registry:
  type: nacos

效果

通过修改配置项registry.type就可以实现eureka和nacos的切换了

相关推荐
BS30813_vx9 小时前
31754+基于 Spring Boot + Vue 的网络文学交流分享平台设计与实现
vue.js·spring boot·后端
蛋先生DX9 小时前
内存安全翻车现场的罪魁祸首,根源就在这三步里?
c++·后端·编程语言
Rain的Java大神之路9 小时前
手写一个Spring容器
java·后端·mysql·spring·面试
IT_陈寒9 小时前
明明设了默认值,为什么我的JavaScript函数参数还是undefined?
前端·人工智能·后端
智购科技自动贩卖机9 小时前
自动售货机嵌入式系统安全加固实践:从Go语言国密算法到硬件防拆的工程化落地
大数据·人工智能·后端·安全·golang·系统安全
烂蜻蜓9 小时前
Flask入门教程(附录):模板渲染API——Jinja2集成全解析
后端·python·flask
摇滚侠9 小时前
《Spring Boot 3:高级与架构设计》第 2 章 IOC容器的高级机制 Environment 阅读笔记 4
spring boot·笔记·后端
豆角焖肉10 小时前
Spring Boot配置文件与自动配置原理深度剖析
spring boot·配置文件·自动配置原理·yaml·conditional
豆角焖肉10 小时前
Spring Boot入门实战:从零搭建第一个应用
java·spring boot·后端·自动配置
BUG指挥官10 小时前
Redis已接入AI
数据库·人工智能·spring boot·redis·spring cloud