【SpringCloud】spring-cloud-loadbalancer 的实现 二
前言
spring-cloud-commos
提供了 loadbalancer
的一方实现 spring-cloud-loadbalancer
,关于从何处看起,我思考了良久,最终决定从 自动装配类
看起,见图
其中:
LoadBalancerAutoConfiguration
是最核心的配置(也是最有意思的一个),本文会重点解读BlockingLoadBalancerClientAutoConfiguration
,上文提到过最终的负载均衡
业务是由LoadBalancerClient
实现的,同时spring-cloud-loadbalancer
也给出了默认实现BlockingLoadBalancerClient
,此类也是针对它的自动装配LoadBalancerCacheAutoConfiguration
,缓存,略OAuth2LoadBalancerClientAutoConfiguration
,貌似是安全相关,略LoadBalancerStatsAutoConfiguration
,暂未了解,略
BlockingLoadBalancerClientAutoConfiguration
@Configuration(proxyBeanMethods = false)
@LoadBalancerClients
@AutoConfigureAfter(LoadBalancerAutoConfiguration.class)
@AutoConfigureBefore({ org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration.class,
AsyncLoadBalancerAutoConfiguration.class })
@ConditionalOnClass(RestTemplate.class)
public class BlockingLoadBalancerClientAutoConfiguration {
@Bean
@ConditionalOnBean(LoadBalancerClientFactory.class)
@ConditionalOnMissingBean
public LoadBalancerClient blockingLoadBalancerClient(LoadBalancerClientFactory loadBalancerClientFactory,
LoadBalancerProperties properties) {
return new BlockingLoadBalancerClient(loadBalancerClientFactory, properties);
}
// ...
}
此配置类的核心功能就是自动装配了 LoadBalancerClient
的实例 BlockingLoadBalancerClient
,其中:
@LoadBalancerClients
,核心注解,会在之后的LoadBalancerAutoConfiguration
中详细了解@AutoConfigureAfter(LoadBalancerAutoConfiguration.class)
,在LoadBalancerAutoConfiguration
之后自动装配(本moudle
而非commons
下的)@AutoConfigureBefore(...)
,在指定类之后自动装配(此moudle
下的装配肯定是优先于commons
的)- 依赖于
LoadBalancerClientFactory
注册了BlockingLoadBalancerClient
,LoadBalancerClientFactory
也是核心类,会在之后的LoadBalancerAutoConfiguration
中详细了解
LoadBalancerAutoConfiguration
@Configuration(proxyBeanMethods = false)
@LoadBalancerClients
@EnableConfigurationProperties(LoadBalancerProperties.class)
@AutoConfigureBefore({ ReactorLoadBalancerClientAutoConfiguration.class,
LoadBalancerBeanPostProcessorAutoConfiguration.class })
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.enabled", havingValue = "true", matchIfMissing = true)
public class LoadBalancerAutoConfiguration {
private final ObjectProvider<List<LoadBalancerClientSpecification>> configurations;
public LoadBalancerAutoConfiguration(ObjectProvider<List<LoadBalancerClientSpecification>> configurations) {
this.configurations = configurations;
}
@Bean
@ConditionalOnMissingBean
public LoadBalancerZoneConfig zoneConfig(Environment environment) {
return new LoadBalancerZoneConfig(environment.getProperty("spring.cloud.loadbalancer.zone"));
}
@ConditionalOnMissingBean
@Bean
public LoadBalancerClientFactory loadBalancerClientFactory() {
LoadBalancerClientFactory clientFactory = new LoadBalancerClientFactory();
clientFactory.setConfigurations(this.configurations.getIfAvailable(Collections::emptyList));
return clientFactory;
}
}
最核心的装配类,它的解读从两个方面展开:
@LoadBalancerClient(s)
LoadBalancerClientFactory
@LoadBalancerClient(s)
@Configuration(proxyBeanMethods = false)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
@Documented
@Import(LoadBalancerClientConfigurationRegistrar.class)
public @interface LoadBalancerClients {
LoadBalancerClient[] value() default {};
Class<?>[] defaultConfiguration() default {};
}
------------------------------------------------
@Configuration(proxyBeanMethods = false)
@Import(LoadBalancerClientConfigurationRegistrar.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LoadBalancerClient {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
Class<?>[] configuration() default {};
}
这两个注解其实就是一回事,它们的核心就是 @Import(LoadBalancerClientConfigurationRegistrar.class)
@LoadBalancerClient
相当于注解声明了一个LoadBalancerClient
,它的信息包括name(value)
即serviceId
,configuration
即对应的负载均衡
配置,至于负载均衡
都包括哪些配置,后文会解读@LoadBalancerClients
就是声明一组LoadBalancerClient
了
那 spring-cloud-loadbalancer
到底是如何做到通过注解来声明不同的 LoadBalancerClient
,并将它们的配置隔离开来呢,其中一部分玄机就在于 LoadBalancerClientConfigurationRegistrar
LoadBalancerClientConfigurationRegistrar
public class LoadBalancerClientConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
/**
* clientName = value | name
* @param client
* @return
*/
private static String getClientName(Map<String, Object> client) {
if (client == null) {
return null;
}
String value = (String) client.get("value");
if (!StringUtils.hasText(value)) {
value = (String) client.get("name");
}
if (StringUtils.hasText(value)) {
return value;
}
throw new IllegalStateException("Either 'name' or 'value' must be provided in @LoadBalancerClient");
}
/**
* 注册的是 LoadBalancerClientSpecification 的实例,同时 configuration
* 作为参数,也就是说每一个 LoadBalancerClient 对应一个 LoadBalancerClientSpecification
* 以 name 隔离
*/
private static void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
Object configuration) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(LoadBalancerClientSpecification.class);
builder.addConstructorArgValue(name);
builder.addConstructorArgValue(configuration);
registry.registerBeanDefinition(name + ".LoadBalancerClientSpecification", builder.getBeanDefinition());
}
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 注解属性
Map<String, Object> attrs = metadata.getAnnotationAttributes(LoadBalancerClients.class.getName(), true);
/**
* 针对每一个 LoadBalancerClient,进行 registerClientConfiguration
*/
if (attrs != null && attrs.containsKey("value")) {
AnnotationAttributes[] clients = (AnnotationAttributes[]) attrs.get("value");
for (AnnotationAttributes client : clients) {
registerClientConfiguration(registry, getClientName(client), client.get("configuration"));
}
}
/**
* 注册 defaultConfiguration
*/
if (attrs != null && attrs.containsKey("defaultConfiguration")) {
String name;
if (metadata.hasEnclosingClass()) {
name = "default." + metadata.getEnclosingClassName();
}
else {
name = "default." + metadata.getClassName();
}
registerClientConfiguration(registry, name, attrs.get("defaultConfiguration"));
}
// 针对 @LoadBalancerClient 的处理,同上
Map<String, Object> client = metadata.getAnnotationAttributes(LoadBalancerClient.class.getName(), true);
String name = getClientName(client);
if (name != null) {
registerClientConfiguration(registry, name, client.get("configuration"));
}
}
}
因为没有尿点,所以贴出所有代码:
- 首先它是一个
ImportBeanDefinitionRegistrar
,因此会在@Configuration
解析的最后阶段,执行registerBeanDefinitions
注册对应的BeanDefinition
registerBeanDefinitions
方法针对每一个@LoadBalancerClient
执行registerClientConfiguration
方法,同时针对@LoadBalancerClients
注册defaultConfiguration
registerClientConfiguration
方法注册的BeanDefinition
,其类型为LoadBalancerClientSpecification
,且configuration
属性的类信息作为参数- 至此,每一个
LoadBalancerClient
的配置信息以clientName
维度被隔离成一个个LoadBalancerClientSpecification
此处的实现与 FeignClient 的隔离实现如出一辙
LoadBalancerClientSpecification
public class LoadBalancerClientSpecification implements NamedContextFactory.Specification {
private String name;
private Class<?>[] configuration;
// ...
}
每一个 LoadBalancerClient
的信息被定义为一个个 LoadBalancerClientSpecification
name
,即对应clientName
configuration
,即对应每个LoadBalancerClient
的负载均衡
配置,至于负载均衡
都包括哪些配置,后文会解读- 父接口
NamedContextFactory.Specification
,其中核心类NamedContextFactory
会在解读LoadBalancerClientFactory
时重点关注
LoadBalancerClientFactory
public class LoadBalancerClientFactory extends NamedContextFactory<LoadBalancerClientSpecification>
implements ReactiveLoadBalancer.Factory<ServiceInstance> {
public static final String NAMESPACE = "loadbalancer";
public static final String PROPERTY_NAME = NAMESPACE + ".client.name";
public LoadBalancerClientFactory() {
super(LoadBalancerClientConfiguration.class, NAMESPACE, PROPERTY_NAME);
}
public String getName(Environment environment) {
// 指定 loadbalancer.client.name 属性可覆盖
return environment.getProperty(PROPERTY_NAME);
}
@Override
public ReactiveLoadBalancer<ServiceInstance> getInstance(String serviceId) {
return getInstance(serviceId, ReactorServiceInstanceLoadBalancer.class);
}
}
继承 NamedContextFactory
,值得一提的是,LoadBalancerAutoConfiguration
自动装配类中收集了容器中所有的 LoadBalancerClientSpecification
,作为构造 LoadBalancerClientFactory
的参数(可以回头看一下 LoadBalancerAutoConfiguration
)
那其实核心类是 NamedContextFactory
,它会拿这些 Specification
干什么呢
NamedContextFactory
NamedContextFactory
代码较多,拆开解读
setConfigurations
private Map<String, C> configurations = new ConcurrentHashMap<>();
public void setConfigurations(List<C> configurations) {
for (C client : configurations) {
this.configurations.put(client.getName(), client);
}
}
将 LoadBalancerClientSpecification
以 clientName
维度开来
getInstance
public <T> T getInstance(String name, Class<T> type) {
AnnotationConfigApplicationContext context = getContext(name);
try {
return context.getBean(type);
}
catch (NoSuchBeanDefinitionException e) {
// ignore
}
return null;
}
基于 getContext
方法获取对应的容器(AnnotationConfigApplicationContext
),然后从容器中获取对应的实例
可以理解为,每一个 LoadBalancerClientSpecification
创建一个对应的 AnnotationConfigApplicationContext
,即最终每一个 LoadBalancerClient
对应一个 容器
,那其中的配置就对应容器中具体的 bean实例
getContext & createContext
protected AnnotationConfigApplicationContext getContext(String name) {
// 缓存
if (!this.contexts.containsKey(name)) {
synchronized (this.contexts) {
if (!this.contexts.containsKey(name)) {
this.contexts.put(name, createContext(name));
}
}
}
return this.contexts.get(name);
}
protected AnnotationConfigApplicationContext createContext(String name) {
// 创建容器对象 AnnotationConfigApplicationContext
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
/**
* 如果有声明对应的 @LoadBalancerClient,则将其下的 负载均衡 配置
* 注册到对应的容器下
*/
if (this.configurations.containsKey(name)) {
for (Class<?> configuration : this.configurations.get(name).getConfiguration()) {
context.register(configuration);
}
}
// 注册 defaultConfigurations
for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
if (entry.getKey().startsWith("default.")) {
for (Class<?> configuration : entry.getValue().getConfiguration()) {
context.register(configuration);
}
}
}
/**
* 注册
* 1)PropertyPlaceholderAutoConfiguration
* 2)默认配置类,此处是 LoadBalancerClientConfiguration
*/
context.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType);
context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName,
Collections.<String, Object>singletonMap(this.propertyName, name)));
// 指定父容器为当前容器
if (this.parent != null) {
context.setParent(this.parent);
context.setClassLoader(this.parent.getClassLoader());
}
context.setDisplayName(generateDisplayName(name));
// 容器启动
context.refresh();
return context;
}
这两个方法放到一块看:
getContext
,基于缓存的获取容器,如果没有,则createContext
- 创建
AnnotationConfigApplicationContext
,并注册对应的configuration
和defaultConfiguration
,指的注意的是,此处是register
而非scan
,换句话说,就是配置类本身被注册为BeanDefinition
而并不解析其中的配置(配置类的解析发生在容器refresh
后解析beanFactoryPostProcessor
阶段) - 同时还会注册一个
LoadBalancerClientConfiguration
(见构造),可以理解为一个兜底配置类,即默认的负载均衡
规则 - 至此,所有
LoadBalancerClient
对应的配置被隔离成一个个AnnotationConfigApplicationContext
放到了LoadBalancerClientFactory
中
总结
以上是本章节内容的流程图总结,到目前为止,我已经了解了:
- 整个
SpringCloud
负载均衡实现的基调:通过LoadBalancerInterceptor
拦截RestTemplate
赋予能力,最终委托LoadBalancerClient
实现 LoadBalancerClient
间的配置是如何做到互相隔离的:通过@LoadBalancerClient
注解生成对应的LoadBalancerClientSpecification
,最终映射成一个个AnnotationConfigApplicationContext
,具体的配置实例就对应其中的bean实例
之后的章节目标就清晰了:
- 负载均衡规则解析
LoadBalancerClient
的调用逻辑