spring · 16 9 月, 2021 0

spring clound负载均衡之Ribbon(二)- 自动装配

在之前的文章spring cloud负载均衡之ribbon—demo实现  通过demo的方式实现了ribbon的使用,在这个实例中,通过round robin的方法轮询访问远程依赖的服务列表,达到负载均衡的方式。在这边文章中,主要介绍ribbon自动装配的过程,以及在装配过程中比较重要的类型,让我们对ribbon有一个整体的设计概念,便于后面我们对ribbon的学习。

前情回顾

spring cloud负载均衡之ribbon—demo实现 中,我们在创建RestTemplate的时候, 使用了@LoadBalanced注解标记了bean,具体代码如下:

@LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

在这段代码中,涉及到了两个类型,第一个就是@LoadBalanced注解以及RestTemplate类型,这两个类在远程访问中充当了比较重要的角色,我们一一来看下。

@LoadBalanced

这个注解标记了访问远程http请求时,需要采用负载均衡的方式发送请求,我们看下该注解的源码:

/**
 * Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient.
 * @author Spencer Gibb
 */
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {

}

在这个注解的实现中,有两个需要注意的点:

  • 根据该类的注释,该注解主要是用于标记RestTemplate bean并且用于使用LoadBalancerClient, 因此可以知道,该注解的使用其实是有使用的限制
  • 该注解是一个派生的注解,被@Qualifier注解修饰。

@Qualifier

在我们常用的场景中,我们一般配合@Autowired注解使用,根据名称获取满足条件的bean, 但是在这里该注解还有一个用法,用于实现自定义注解。具体源码如下:

/**
 * This annotation may be used on a field or parameter as a qualifier for
 * candidate beans when autowiring. It may also be used to annotate other
 * custom annotations that can then in turn be used as qualifiers.
 *
 * @author Mark Fisher
 * @author Juergen Hoeller
 * @since 2.5
 * @see Autowired
 */
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {

    String value() default "";

}

通过源码可以得知,该注解的另外一个用于就是自定义注解,自动注入满足自定义注解的bean类型。

LoadBalancerClient

在上面介绍@LoadBalanced注解时有介绍到,会涉及到LoadBalancerClient类,该类是一个接口定义, 由spring-cloud-commons实现, 其实就是抽象微服务部分的抽象属性,便于对接不同的微服务组件。具体源码如下:

public interface LoadBalancerClient extends ServiceInstanceChooser {

    <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;

    <T> T execute(String serviceId, ServiceInstance serviceInstance,
            LoadBalancerRequest<T> request) throws IOException;

    URI reconstructURI(ServiceInstance instance, URI original);

}

RibbonAutoConfiguration

在ribbon加载并配置时,一般都需要查看spring.factories文件,具体配置如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration

可以知道自动加载配置时,会通过RibbonAutoConfiguration类进行ribbon的基本配置,因此我们直接查看该类源码即可。

加载条件

加载条件一般是通过类上的注解信息进行标注,因此我们优先查看该类上的注解列表,查看该类配置需要满足条件,以及加载顺序。

@Configuration
@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
@RibbonClients
@AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,AsyncLoadBalancerAutoConfiguration.class })
@EnableConfigurationProperties({ RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class })

该注解列表可以表达一下几个信息:

  • 该类被加载,需要在classpath中找到RibbonClassesConditions
  • 该配置需要在EurekaClientAutoConfiguration配置之后执行配置
  • 当前配置完成后,需要配置LoadBalancerAutoConfigurationAsyncLoadBalancerAutoConfiguration
  • 加载并初始化配置RibbonEagerLoadPropertiesServerIntrospectorProperties

配置类加载

通过以上配置可以得知, 需要加载两个配置类型,分别为RibbonEagerLoadPropertiesServerIntrospectorProperties

RibbonEagerLoadProperties

查看该配置类型的源码:

@ConfigurationProperties(prefix = "ribbon.eager-load")
public class RibbonEagerLoadProperties {

    private boolean enabled = false;

    private List<String> clients;
}

该配置类主要保存以ribbon.eager-load.*相关的配置,配置总结如下:

配置 默认值 说明
ribbon.eager-load.enabled false 是否开启ribbon实时加载
ribbon.eager-load.clients null ribbon实时记载客户端列表

ServerIntrospectorProperties

查看该类源码如下:

@ConfigurationProperties("ribbon")
public class ServerIntrospectorProperties {

    private List<Integer> securePorts = Arrays.asList(443, 8443);

    public List<Integer> getSecurePorts() {
        return securePorts;
    }

    public void setSecurePorts(List<Integer> securePorts) {
        this.securePorts = securePorts;
    }
}

该配置类主要用于保存以ribbon.*相关的配置信息,但是从源码角度查看,该类实际只是保存了ribbon.secure-ports列表列表,该配置默认值为[443, 8443]

配置中

当以上条件都满足后, 并且配置类型加载完成,则会进入RibbonAutoConfiguration类型的正式配置阶段。EurekaClientAutoConfiguation配置可以参考spring cloud 服务发现之Eureka Client(二)—自动装配

在配置过程中,主要涉及到基本比较重要的类型,其中包括SpringClientFactoryLoadBalancerClientLoadBalancedRetryFactoryPropertiesFactoryRestTemplateCustomizerRibbonApplicationContextInitilizer. 具体源码如下:

public class RibbonAutoConfiguration {

    @Autowired(required = false)
    private List<RibbonClientSpecification> configurations = new ArrayList<>();

    @Autowired
    private RibbonEagerLoadProperties ribbonEagerLoadProperties;

    @Bean
    public HasFeatures ribbonFeature() {
        return HasFeatures.namedFeature("Ribbon", Ribbon.class);
    }

    // 创建SpringClientFactory对象
    @Bean
    public SpringClientFactory springClientFactory() {
        SpringClientFactory factory = new SpringClientFactory();
        factory.setConfigurations(this.configurations);
        return factory;
    }

    // 当LoadBalancerClient对象不存在时,创建LoadBalancerClient对象
    @Bean
    @ConditionalOnMissingBean(LoadBalancerClient.class)
    public LoadBalancerClient loadBalancerClient() {
        return new RibbonLoadBalancerClient(springClientFactory());
    }

    // 判断classpath中是否包含了RetryTemplate类,如果存在,并且LoadBalancedRetryFactory不能存在时,则创建LoadBalancedRetryFactory对象
    @Bean
    @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
    @ConditionalOnMissingBean
    public LoadBalancedRetryFactory loadBalancedRetryPolicyFactory(
            final SpringClientFactory clientFactory) {
        return new RibbonLoadBalancedRetryFactory(clientFactory);
    }

    // 当确实PropertiesFactory对象不存在时,则创建PropertiesFactory对象
    @Bean
    @ConditionalOnMissingBean
    public PropertiesFactory propertiesFactory() {
        return new PropertiesFactory();
    }

    // 当ribbon.eager-load.enabled的配置值为true时,创建RibbonApplicationContextInitializer对象
    @Bean
    @ConditionalOnProperty("ribbon.eager-load.enabled")
    public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() {
        return new RibbonApplicationContextInitializer(springClientFactory(),
                ribbonEagerLoadProperties.getClients());
    }

    // 该配置类型的装配,需要满足两个条件
    // 1. classpath中包含了HttpRequest类
    // 2. ribbon.restclient.enabled配置为true
    @Configuration
    @ConditionalOnClass(HttpRequest.class)
    @ConditionalOnRibbonRestClient
    protected static class RibbonClientHttpRequestFactoryConfiguration {

        @Autowired
        private SpringClientFactory springClientFactory;

        // 创建RestTemplateCustomizer对象
        @Bean
        public RestTemplateCustomizer restTemplateCustomizer(
                final RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory) {
            return restTemplate -> restTemplate
                    .setRequestFactory(ribbonClientHttpRequestFactory);
        }

        // 创建RibbonClientHttpRequestFactory对象
        @Bean
        public RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory() {
            return new RibbonClientHttpRequestFactory(this.springClientFactory);
        }

    }

    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Conditional(OnRibbonRestClientCondition.class)
    @interface ConditionalOnRibbonRestClient {

    }

    private static class OnRibbonRestClientCondition extends AnyNestedCondition {

        OnRibbonRestClientCondition() {
            super(ConfigurationPhase.REGISTER_BEAN);
        }

        @Deprecated // remove in Edgware"
        @ConditionalOnProperty("ribbon.http.client.enabled")
        static class ZuulProperty {

        }

        @ConditionalOnProperty("ribbon.restclient.enabled")
        static class RibbonProperty {

        }

    }

}

LoadBalancerAutoConfiguration

该类加载是在RibbonAutoConfiguration装配完成后执行,该类的装在也是有条件的,所以我们可以具体看下。

条件判断

@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)

通过注解头的条件声明,可以看到,该类的装配需要满足一下条件:

  • 在classpath中包含了RestTemplate类
  • 在spring容器中包含了LoadBalancerClient对象
  • 自动加载LoadBalancerRetryProperites配置

装载中

在该配置类型的装在过程中,最主要的部分则是生成LoadBalancerRequestFactorySmartInitialzingSingleton的bean实例。同事判断是否包含了RetryTemplate, 一次做增量的装配。具体源码如下

@LoadBalanced
    @Autowired(required = false)
    private List<RestTemplate> restTemplates = Collections.emptyList();

    @Autowired(required = false)
    private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();

    // 创建SmartInitializingSingleton bean实例
    @Bean
    public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
            final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
        return () -> restTemplateCustomizers.ifAvailable(customizers -> {
            for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
                for (RestTemplateCustomizer customizer : customizers) {
                    customizer.customize(restTemplate);
                }
            }
        });
    }

    // 创建LoadBalancerRequestFactory 实例
    @Bean
    @ConditionalOnMissingBean
    public LoadBalancerRequestFactory loadBalancerRequestFactory(
            LoadBalancerClient loadBalancerClient) {
        return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
    }

同时会根据在classpath中依赖包的变化,动态扩展加载,具体源码如下:

    // 判断在classpath中是否包含了RetryTemplate类, 如果不包含,则装配LoadBalancerInterceptorConfig 
    @Configuration
    @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
    static class LoadBalancerInterceptorConfig {

        // 创建LoadBalancerInterceptor
        @Bean
        public LoadBalancerInterceptor ribbonInterceptor(
                LoadBalancerClient loadBalancerClient,
                LoadBalancerRequestFactory requestFactory) {
            return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
        }

        // 创建RestTemplateCustomizer
        @Bean
        @ConditionalOnMissingBean
        public RestTemplateCustomizer restTemplateCustomizer(
                final LoadBalancerInterceptor loadBalancerInterceptor) {
            return restTemplate -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                        restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list);
            };
        }

    }

    /**
     * Auto configuration for retry mechanism.
     */
    @Configuration
    @ConditionalOnClass(RetryTemplate.class)
    public static class RetryAutoConfiguration {

        // 创建LoadBalancedRetryFactory对象
        @Bean
        @ConditionalOnMissingBean
        public LoadBalancedRetryFactory loadBalancedRetryFactory() {
            return new LoadBalancedRetryFactory() {
            };
        }

    }

    /**
     * Auto configuration for retry intercepting mechanism.
     */
    @Configuration
    @ConditionalOnClass(RetryTemplate.class)
    public static class RetryInterceptorAutoConfiguration {

        // 创建RetryLoadBalancerInterceptor对象
        @Bean
        @ConditionalOnMissingBean
        public RetryLoadBalancerInterceptor ribbonInterceptor(
                LoadBalancerClient loadBalancerClient,
                LoadBalancerRetryProperties properties,
                LoadBalancerRequestFactory requestFactory,
                LoadBalancedRetryFactory loadBalancedRetryFactory) {
            return new RetryLoadBalancerInterceptor(loadBalancerClient, properties,
                    requestFactory, loadBalancedRetryFactory);
        }

        // 创建RestTemplateCustomizer对象
        @Bean
        @ConditionalOnMissingBean
        public RestTemplateCustomizer restTemplateCustomizer(
                final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
            return restTemplate -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                        restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list);
            };
        }

    }

AsyncLoadBalancerAutoConfiguration

该装配类型也是后置装配中的一个重要的类, 该类同样也需要满足条件后才能开始装配。

条件判断

@Configuration
@ConditionalOnBean(LoadBalancerClient.class)
@ConditionalOnClass(AsyncRestTemplate.class)

该类的装配条件需要满足两个方面:

  • 在classpath中包含了SyncRestTemplate类
  • 在spring容器中包含了LoadBalancerClient对象

装配中

装配中包含了两个内部的配置类型,具体源码如下;

AsyncRestTemplateCustomizerConfig

@Configuration
    static class AsyncRestTemplateCustomizerConfig {

        // 注入所有的AsyncRestTemplate bean
        @LoadBalanced
        @Autowired(required = false)
        private List<AsyncRestTemplate> restTemplates = Collections.emptyList();

        // 创建SmartInitializingSingleton对象
        @Bean
        public SmartInitializingSingleton loadBalancedAsyncRestTemplateInitializer(
                final List<AsyncRestTemplateCustomizer> customizers) {
            return new SmartInitializingSingleton() {
                @Override
                public void afterSingletonsInstantiated() {
                    for (AsyncRestTemplate restTemplate : AsyncRestTemplateCustomizerConfig.this.restTemplates) {
                        for (AsyncRestTemplateCustomizer customizer : customizers) {
                            customizer.customize(restTemplate);
                        }
                    }
                }
            };
        }

    }

LoadBalancerInterceptorConfig

@Configuration
    static class LoadBalancerInterceptorConfig {

                // 创建AsyncLoadBalancerInterceptor bean
        @Bean
        public AsyncLoadBalancerInterceptor asyncLoadBalancerInterceptor(
                LoadBalancerClient loadBalancerClient) {
            return new AsyncLoadBalancerInterceptor(loadBalancerClient);
        }

                // AsyncRestTemplateCustomizer bean
        @Bean
        public AsyncRestTemplateCustomizer asyncRestTemplateCustomizer(
                final AsyncLoadBalancerInterceptor loadBalancerInterceptor) {
            return new AsyncRestTemplateCustomizer() {
                @Override
                public void customize(AsyncRestTemplate restTemplate) {
                    List<AsyncClientHttpRequestInterceptor> list = new ArrayList<>(
                            restTemplate.getInterceptors());
                    list.add(loadBalancerInterceptor);
                    restTemplate.setInterceptors(list);
                }
            };
        }

 

以上就是spring cloud ribbon自动装配的过程。该篇文章只是介绍在自动装配中的过程,不涉及具体的业务逻辑,后续章节将介绍在ribbon中每个类的作用,以及具体的代码实现细节。