AI智能
改变未来

SpringCloud升级之路2020.0.x版-22.Spring Cloud LoadBalancer核心源码

本系列代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford

经过上一节的详细分析,我们知道可以通过

LoadBalancerClientFactory

知道默认配置类

LoadBalancerClientConfiguration

. 并且获取微服务名称可以通过

environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);

LoadBalancerClientFactory

public static final String NAMESPACE = "loadbalancer";public static final String PROPERTY_NAME = NAMESPACE + ".client.name";public LoadBalancerClientFactory() {super(LoadBalancerClientConfiguration.class, NAMESPACE, PROPERTY_NAME);}

查看配置类

LoadBalancerClientConfiguration

,我们可以发现这个类主要定义两种 Bean,分别是

ReactorLoadBalancer<ServiceInstance>

ServiceInstanceListSupplier

ReactorLoadBalancer

是负载均衡器,主要提供根据服务名称获取服务实例列表并从从中选择的功能。

ReactorLoadBalancer

Mono<Response<T>> choose(Request request);

在默认配置中的实现是:

LoadBalancerClientConfiguration

@Bean@ConditionalOnMissingBeanpublic ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,LoadBalancerClientFactory loadBalancerClientFactory) {//获取微服务名称String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);//创建 RoundRobinLoadBalancer//注意这里注入的是 LazyProvider,这主要因为在注册这个 Bean 的时候相关的 Bean 可能还没有被加载注册,利用 LazyProvider 而不是直接注入所需的 Bean 防止报找不到 Bean 注入的错误。return new RoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name,ServiceInstanceListSupplier.class), name);}

可以看出,默认配置的

ReactorLoadBalancer

实现是

RoundRobinLoadBalancer

。这个负载均衡器实现很简单,有一个原子类型的

AtomicInteger position

,从

ServiceInstanceListSupplier

中读取所有的服务实例列表,然后对于

position

原子加1,对列表大小取模,返回列表中这个位置的服务实例

ServiceInstance

RoundRobinLoadBalancer

public Mono<Response<ServiceInstance>> choose(Request request) {//注入的时候注入的是 Lazy Provider,这里取出真正的 Bean,也就是 ServiceInstanceListSupplierServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);//获取实例列表return supplier.get(request).next()//从列表中选择一个实例.map(serviceInstances -> processInstanceResponse(supplier, serviceInstances));}private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier,List<ServiceInstance> serviceInstances) {Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(serviceInstances);// 如果 ServiceInstanceListSupplier 也实现了 SelectedInstanceCallback,则执行下面的逻辑进行回调。SelectedInstanceCallback 就是每次负载均衡器选择实例之后进行的回调if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());}return serviceInstanceResponse;}private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {if (instances.isEmpty()) {return new EmptyResponse();}//postion 原子 +1 并取绝对值int pos = Math.abs(this.position.incrementAndGet());//返回对应下标的实例ServiceInstance instance = instances.get(pos % instances.size());return new DefaultResponse(instance);}

ServiceInstanceListSupplier

是服务列表提供者接口:

ServiceInstanceListSupplier

public interface ServiceInstanceListSupplier extends Supplier<Flux<List<ServiceInstance>>> {String getServiceId();default Flux<List<ServiceInstance>> get(Request request) {return get();}static ServiceInstanceListSupplierBuilder builder() {return new ServiceInstanceListSupplierBuilder();}}

spring-cloud-loadbalancer 中有很多

ServiceInstanceListSupplier

的实现,在默认配置中是通过属性配置指定实现的,这个配置项是

spring.cloud.loadbalancer.configurations

。例如:

LoadBalancerClientConfiguration

@Bean@ConditionalOnBean(ReactiveDiscoveryClient.class)@ConditionalOnMissingBean//spring.cloud.loadbalancer.configurations 未指定或者为 default@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "default",matchIfMissing = true)public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(ConfigurableApplicationContext context) {return ServiceInstanceListSupplier.builder()//通过 DiscoveryClient 提供实例.withDiscoveryClient()//开启缓存.withCaching().build(context);}@Bean@ConditionalOnBean(ReactiveDiscoveryClient.class)@ConditionalOnMissingBean//如果 spring.cloud.loadbalancer.configurations 指定为 zone-preference@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "zone-preference")public ServiceInstanceListSupplier zonePreferenceDiscoveryClientServiceInstanceListSupplier(ConfigurableApplicationContext context) {return ServiceInstanceListSupplier.builder()//通过 DiscoveryClient 提供实例.withDiscoveryClient()//启用更倾向于同一个 zone 下实例的特性.withZonePreference()//开启缓存.withCaching().build(context);}

可以看到,可以通过

ServiceInstanceListSupplier.builder()

生成官方封装好各种特性的

ServiceInstanceListSupplier

。其实从底层实现可以看出,所有的

ServiceInstanceListSupplier

实现都是代理模式,例如对于默认配置,底层代码近似于:

return  //开启服务实例缓存new CachingServiceInstanceListSupplier(//启用通过 discoveryClient 的服务发现new DiscoveryClientServiceInstanceListSupplier(discoveryClient, env), cacheManagerProvider.getIfAvailable());

除了默认配置

LoadBalancerClientConfiguration

,用户配置自定义配置则是通过

@LoadBalancerClients

@LoadBalancerClient

.这个原理是通过

LoadBalancerClientConfigurationRegistrar

实现的。首先,我们来看一下

LoadBalancerClientFactory

这个

NamedContextFactory

是如何创建的:

[

LoadBalancerAutoConfiguration

]

private final ObjectProvider<List<LoadBalancerClientSpecification>> configurations;public LoadBalancerAutoConfiguration(ObjectProvider<List<LoadBalancerClientSpecification>> configurations) {//注入 LoadBalancerClientSpecification List 的 provider//在 Bean 创建的时候,进行载入,而不是注册的时候this.configurations = configurations;}@ConditionalOnMissingBean@Beanpublic LoadBalancerClientFactory loadBalancerClientFactory() {//创建 LoadBalancerClientFactoryLoadBalancerClientFactory clientFactory = new LoadBalancerClientFactory();//读取所有的 LoadBalancerClientSpecification,设置为 LoadBalancerClientFactory 的配置clientFactory.setConfigurations(this.configurations.getIfAvailable(Collections::emptyList));return clientFactory;}

那么,

LoadBalancerClientSpecification

这些 Bean 是怎么创建的呢?在

@LoadBalancerClients

@LoadBalancerClient

注解中,都包含

@Import(LoadBalancerClientConfigurationRegistrar.class)

。这个

@Import

加载一个

ImportBeanDefinitionRegistrar

,这里是

LoadBalancerClientConfigurationRegistrar

.

ImportBeanDefinitionRegistrar

里面的方法参数包含注解元数据,以及注册 Bean 的

BeanDefinitionRegistry

。一般通过注解元数据,动态通过

BeanDefinitionRegistry

注册 Bean,在这里的实现是:

[

LoadBalancerClients

]

@Configuration(proxyBeanMethods = false)@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.TYPE })@Documented@Import(LoadBalancerClientConfigurationRegistrar.class)public @interface LoadBalancerClients {//可以指定多个 LoadBalancerClientLoadBalancerClient[] value() default {};//指定所有的负载均衡配置的默认配置Class<?>[] defaultConfiguration() default {};}

[

LoadBalancerClient

]

@Configuration(proxyBeanMethods = false)@Import(LoadBalancerClientConfigurationRegistrar.class)@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface LoadBalancerClient {//name 和 value 都是微服务名称@AliasFor("name")String value() default "";@AliasFor("value")String name() default "";//这个微服务的配置Class<?>[] configuration() default {};}

[

LoadBalancerClientConfigurationRegistrar

]

@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {//获取 LoadBalancerClients 注解的元数据Map<String, Object> attrs = metadata.getAnnotationAttributes(LoadBalancerClients.class.getName(), true);if (attrs != null && attrs.containsKey("value")) {AnnotationAttributes[] clients = (AnnotationAttributes[]) attrs.get("value");//对于 value 属性,其实就是一个 LoadBalancerClient 列表,对于每个生成一个特定微服务名字的  LoadBalancerClientSpecificationfor (AnnotationAttributes client : clients) {registerClientConfiguration(registry, getClientName(client), client.get("configuration"));}}//如果指定了 defaultConfiguration,则注册为 default 的配置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"));}}private static void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {//初始化 LoadBalancerClientSpecification 的 BeanDefinition,用于注册一个 LoadBalancerClientSpecification BeanBeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(LoadBalancerClientSpecification.class);//构造器参数builder.addConstructorArgValue(name);builder.addConstructorArgValue(configuration);//注册 Beanregistry.registerBeanDefinition(name + ".LoadBalancerClientSpecification", builder.getBeanDefinition());}

从代码中我们可以看出,通过使用

@LoadBalancerClients

@LoadBalancerClient

注解可以自动生成对应的

LoadBalancerClientSpecification

进而实现公共负载均衡配置或者特定某个微服务的负载均衡配置。

我们这一节详细分析 Spring Cloud LoadBalancer 的源代码来理解其中的原理,下一节我们将介绍在我们项目中使用 Spring Cloud LoadBalancer 要实现的功能。

微信搜索“我的编程喵”关注公众号,每日一刷,轻松提升技术,斩获各种offer

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » SpringCloud升级之路2020.0.x版-22.Spring Cloud LoadBalancer核心源码