共计 6601 个字符,预计需要花费 17 分钟才能阅读完成。
前言
前段时间业务部门有这么一个业务场景,他们本人微服务注册核心是用 eureka,他们有一些服务接口要调用兄弟部门的接口,他们定了一个服务调用计划,业务部门间接把他们服务注册到兄弟部门的注册核心,而后走 rpc 调用,兄弟部门注册核心是用 nacos。
一开始业务部门研发间接在在 pom.xml 这么引入
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
而后我的项目启动报了如下错
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/NacosDiscoveryAutoConfiguration.class]
- eurekaRegistration: defined in BeanDefinition defined in class path resource [org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration$RefreshableEurekaClientConfiguration.class]
业务部门是这么解决的,每次发版时,如果是要纳入兄弟部门的微服务,他们就先手动正文掉 eureka 的客户端依赖。
起初业务部门就向咱们部门提了一个需要,pom 引入多个注册核心客户端,我的项目也要能失常启动
需要剖析
从我的项目异样剖析
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/NacosDiscoveryAutoConfiguration.class]
- eurekaRegistration: defined in BeanDefinition defined in class path resource [org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration$RefreshableEurekaClientConfiguration.class]
能够看出 springcloud 默认的服务注册是只反对单服务注册核心。因而咱们解决的计划要么扩大 springcloud 源码,让它反对多注册核心,要么就是通知 springcloud 当存在多种注册核心客户端时,抉择一种咱们想要的注册核心客户端
本文就选实现绝对容易的计划,当存在多种注册核心客户端时,咱们通知 springcloud,咱们想选的注册核心
实现计划
目前根本只有和 springboot 集成的开源我的项目,能够说大部分应用了主动拆卸,因而咱们的解决思路也是从主动拆卸搞起
前置常识
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean[] skip = new boolean[candidates.length];
boolean skipped = false;
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {invokeAwareMethods(filter);
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {if (!match[i]) {skip[i] = true;
candidates[i] = null;
skipped = true;
}
}
}
if (!skipped) {return configurations;}
List<String> result = new ArrayList<>(candidates.length);
for (int i = 0; i < candidates.length; i++) {if (!skip[i]) {result.add(candidates[i]);
}
}
if (logger.isTraceEnabled()) {int numberFiltered = configurations.size() - result.size();
logger.trace("Filtered" + numberFiltered + "auto configuration class in"
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + "ms");
}
return new ArrayList<>(result);
}
这两个代码片段,一个是主动拆卸的代码片段,一个是过滤候选哪些不须要进行主动拆卸
计划一:利用 AutoConfigurationImportFilter + 自定义标识
实现的原理: 当自定的标识为 nacos,通过 AutoConfigurationImportFilter 排除 eureka 的主动拆卸;反之排除 nacos 的主动拆卸
1、外围实现
public class RegistrationCenterAutoConfigurationImportFilter implements AutoConfigurationImportFilter, EnvironmentAware {
private Environment environment;
/**
* 因为 springboot 主动拆卸,默认会把 spring.factories 的配置的类先全副加载到候选汇合中,* 因而当咱们代码配置启用 nacos,则需把其余注册核心,比方 eureka 先从候选汇合排除
* @param autoConfigurationClasses
* @param autoConfigurationMetadata
* @return
*/
@Override
public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {Set<String> activeProfiles = Arrays.stream(environment.getActiveProfiles()).collect(Collectors.toSet());
if(activeProfiles.contains(RegistrationCenterConstants.ACTIVE_PROFILE_NACOS)){return excludeMissMatchRegistrationAutoConfiguration(autoConfigurationClasses, new String[]{RegistrationCenterConstants.EUREKA_AUTO_CONFIGURATION_CLASSES});
}else if (activeProfiles.contains(RegistrationCenterConstants.ACTIVE_PROFILE_EUREKA)){return excludeMissMatchRegistrationAutoConfiguration(autoConfigurationClasses, new String[]{RegistrationCenterConstants.NACOS_DISCOVERY_AUTO_CONFIGURATION_CLASSES,RegistrationCenterConstants.NACOS_DISCOVERY_CLIENT_AUTO_CONFIGURATION_CLASSES,RegistrationCenterConstants.NACOS_DISCOVERY_ENDPOINT_AUTO_CONFIGURATION_CLASSES});
}
return new boolean[0];
}
private boolean[] excludeMissMatchRegistrationAutoConfiguration(String[] autoConfigurationClasses,String[] needExcludeRegistrationAutoConfigurationClasse) {Map<String,Boolean> needExcludeRegistrationAutoConfigurationClzMap = initNeedExcludeRegistrationAutoConfigurationClzMap(needExcludeRegistrationAutoConfigurationClasse);
boolean[] match = new boolean[autoConfigurationClasses.length];
for (int i = 0; i < autoConfigurationClasses.length; i++) {String autoConfigurationClz = autoConfigurationClasses[i];
match[i] = !needExcludeRegistrationAutoConfigurationClzMap.containsKey(autoConfigurationClz);
}
return match;
}
private Map<String,Boolean> initNeedExcludeRegistrationAutoConfigurationClzMap(String[] needExcludeRegistrationAutoConfigurationClasse){Map<String,Boolean> needExcludeRegistrationAutoConfigurationClzMap = new HashMap<>();
for (String needExcludeRegistrationAutoConfigurationClz : needExcludeRegistrationAutoConfigurationClasse) {needExcludeRegistrationAutoConfigurationClzMap.put(needExcludeRegistrationAutoConfigurationClz,false);
}
return needExcludeRegistrationAutoConfigurationClzMap;
}
@Override
public void setEnvironment(Environment environment) {this.environment = environment;}
}
2、配置 spring.factories
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
com.github.lybgeek.registration.autoconfigure.filter.RegistrationCenterAutoConfigurationImportFilter
计划二:利用 application-${指定注册核心标识} + spring.profiles.active
1、在要激活的注册核心的文件禁用其余注册核心客户端
比方 appliication-nacos.yml 禁用 eureka
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
# 禁用 eureka 客户端主动注册
eureka:
client:
enabled: false
2、激活想要注册的注册核心
spring:
application:
name: springboot-registration-client
profiles:
active: nacos
总结
这两种计划集体是比拟举荐计划二,因为改变最小。计划一比拟实用于没有提供是否须要激活注册核心开关的注册核心。其次如果咱们要排除某些开源主动拆卸的组件,也能够思考用计划一
demo 链接
https://github.com/lyb-geek/springboot-learning/tree/master/springboot-registrationcenter-switch