在之前的文章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
配置之后执行配置 - 当前配置完成后,需要配置
LoadBalancerAutoConfiguration
与AsyncLoadBalancerAutoConfiguration
- 加载并初始化配置
RibbonEagerLoadProperties
与ServerIntrospectorProperties
配置类加载
通过以上配置可以得知, 需要加载两个配置类型,分别为RibbonEagerLoadProperties
、ServerIntrospectorProperties
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(二)—自动装配
在配置过程中,主要涉及到基本比较重要的类型,其中包括SpringClientFactory
、LoadBalancerClient
、LoadBalancedRetryFactory
、PropertiesFactory
、RestTemplateCustomizer
、 RibbonApplicationContextInitilizer
. 具体源码如下:
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配置
装载中
在该配置类型的装在过程中,最主要的部分则是生成LoadBalancerRequestFactory
、SmartInitialzingSingleton
的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中每个类的作用,以及具体的代码实现细节。