【SpringCloud】spring-cloud-loadbalancer 的实现 二

前言

spring-cloud-commos 提供了 loadbalancer 的一方实现 spring-cloud-loadbalancer,关于从何处看起,我思考了良久,最终决定从 自动装配类 看起,见图
spring.factories
其中:

  • 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 注册了 BlockingLoadBalancerClientLoadBalancerClientFactory 也是核心类,会在之后的 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)serviceIdconfiguration 即对应的 负载均衡 配置,至于 负载均衡 都包括哪些配置,后文会解读
  • @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);
		}
	}

LoadBalancerClientSpecificationclientName 维度开来

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,并注册对应的 configurationdefaultConfiguration,指的注意的是,此处是 register 而非 scan,换句话说,就是配置类本身被注册为 BeanDefinition 而并不解析其中的配置(配置类的解析发生在容器 refresh 后解析 beanFactoryPostProcessor 阶段)
  • 同时还会注册一个 LoadBalancerClientConfiguration(见构造),可以理解为一个兜底配置类,即默认的 负载均衡 规则
  • 至此,所有 LoadBalancerClient 对应的配置被隔离成一个个 AnnotationConfigApplicationContext 放到了 LoadBalancerClientFactory

总结

流程图

以上是本章节内容的流程图总结,到目前为止,我已经了解了:

  1. 整个 SpringCloud 负载均衡实现的基调:通过 LoadBalancerInterceptor 拦截 RestTemplate 赋予能力,最终委托 LoadBalancerClient 实现
  2. LoadBalancerClient 间的配置是如何做到互相隔离的:通过 @LoadBalancerClient 注解生成对应的 LoadBalancerClientSpecification,最终映射成一个个 AnnotationConfigApplicationContext,具体的配置实例就对应其中的 bean实例

之后的章节目标就清晰了:

  • 负载均衡规则解析
  • LoadBalancerClient 的调用逻辑

上一篇:【SpringCloud】spring-cloud-loadbalancer 的实现 一

下一篇:【SpringCloud】spring-cloud-loadbalancer 的实现 三

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值