在之前的文章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 {
}
在这个注解的实现中,有两个需要注意的点:
- 根据该类的注释,该注解主要是用于标记
RestTemplatebean并且用于使用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中每个类的作用,以及具体的代码实现细节。