(1). 概述
两年前,在看Ribbon源码时,没有花时间记录源码,原因,赶时间,Ribbon其实并不简单,我为什么这么说呢?因为:Ribbon负载均衡是为每一个serviceId(微服务名称)生成一个:ApplicationContext,为什么要这样做?我们可以针对不同的微服务,配置不同的策略,如果配置在一起的话,就会相互干扰呢?
(2). NamedContextFactory属性分析
public abstract class NamedContextFactory<C extends NamedContextFactory.Specification> implements
DisposableBean,
ApplicationContextAware {
public interface Specification {
String getName();
Class<?>[] getConfiguration();
}
// ********************************************************************************************
// key : 是微服务名称
// value : AnnotationConfigApplicationContext
// ********************************************************************************************
private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();
// ********************************************************************************************
// key : 微服务名称
// value : 配置的Class(比如:com.alibaba.cloud.nacos.ribbon.NacosRibbonClientConfiguration)
// ********************************************************************************************
private Map<String, C> configurations = new ConcurrentHashMap<>();
// ********************************************************************************************
// 为容器配置父容器
// ********************************************************************************************
private ApplicationContext parent;
// ********************************************************************************************
// org.springframework.cloud.openfeign.FeignClientsConfiguration
// ********************************************************************************************
private Class<?> defaultConfigType;
// ribbon
private final String propertySourceName;
// ribbon.client.name
private final String propertyName;
}
(3). NamedContextFactory.getContext
protected AnnotationConfigApplicationContext getContext(String name) {
if (!this.contexts.containsKey(name)) {
synchronized (this.contexts) {
if (!this.contexts.containsKey(name)) {
// ************************************************************
// name为微服务的名称
// ************************************************************
this.contexts.put(name, createContext(name));
}
}
}
return this.contexts.get(name);
}
(4). NamedContextFactory.createContext
protected AnnotationConfigApplicationContext createContext(String name) {
// ***********************************************************************************
// name : 为微服务称
// 为每一个微服务创建一个ApplicationContext来着
// ***********************************************************************************
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 处理注解上配置的configuration
// 比如:
// name : test-provider
// configuration : help.lixin.route.ribbon.config.EurekaRibbonClientConfig
// @RibbonClients(defaultConfiguration=EurekaRibbonClientConfig.class)
if (this.configurations.containsKey(name)) {
for (Class<?> configuration : this.configurations.get(name).getConfiguration()) {
// 这一步,实际是往Spring容器里添加配置对象(可以理解成Spring以前的一段XML即可)
context.register(configuration);
}
}
// 处理注解上配置的configuration
// 比如:
// name : default.help.lixin.route.ribbon.config.NacosRibbonClientConfig
// configuration : help.lixin.route.ribbon.config.NacosRibbonClientConfig
// @RibbonClients(defaultConfiguration=NacosRibbonClientConfig.class)
for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
if (entry.getKey().startsWith("default.")) {
for (Class<?> configuration : entry.getValue().getConfiguration()) {
context.register(configuration);
}
}
}
// 上面的处理,实际就是往Spring容器里添加一堆的Bean定义信息
// defaultConfigType = FeignClientsConfiguration
// defaultConfigType = LoadBalancerClientConfiguration
// 向Spring容器中注册多个Bean
context.register(PropertyPlaceholderAutoConfiguration.class,this.defaultConfigType);
// *********************************************************************************
// 向Spring容器中注册配置信息,配置的名称不重要,重要的是:MapPropertySource里内部Hold住的Map
// MapPropertySource = {
// ribbon.client.name : test-provider
// }
// *********************************************************************************
context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
this.propertySourceName,
// propertyName = ribbon.client.name
// name = test-provider(微服务的名称)
Collections.<String, Object> singletonMap(this.propertyName, name)));
if (this.parent != null) {
// Uses Environment from parent as well as beans
context.setParent(this.parent);
}
context.setDisplayName(generateDisplayName(name));
context.refresh();
return context;
}
(5). RibbonClientConfiguration
对上面的内容好奇吗?为什么是往配置里加的是一条信息?
@Configuration
@EnableConfigurationProperties
@Import({HttpClientConfiguration.class, OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
public class RibbonClientConfiguration {
// *****************************************************************
// 重点就在于这里:会把服务名称(test-provider)注入到这里来
// *****************************************************************
@RibbonClientName
private String name = "client";
// ... ...
}
(6). RibbonClientName注解
// *****************************************************************
// 与上面的遥相呼应.
// *****************************************************************
@Value("${ribbon.client.name}")
public @interface RibbonClientName {
}
(7). 总结
Spring的设计还是挺优雅的,通过不同上下文可以做到完全隔离,但是,也增加了复杂度,一般开发人员估计难理解.