深入理解@LoadBalanced注解的实现原理与客户端负载均衡
前提#
在阅读这篇博客之前,希望你对SpringCloud套件熟悉和理解,更希望关注下微服务开发平台
概述#
在使用springcloud ribbon客户端负载均衡的时候,可以给RestTemplate bean 加一个@LoadBalanced注解,就能让这个RestTemplate在请求时拥有客户端负载均衡的能力,先前有细嚼过但是没有做过笔记,刚好处理此类问题记录下
@LoadBalanced#
Copy
/**
* 注释将RestTemplate bean标记为配置为使用LoadBalancerClient。
*/
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
通过源码可以发现这是一个LoadBalanced标记注解并且标记了@Qualifier(基于Spring Boot的自动配置机制),我们可以溯源到LoadBalancerAutoConfiguration
LoadBalancerAutoConfiguration#
Copy
/**
* 功能区的自动配置(客户端负载平衡)
*/
@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
@LoadBalanced
@Autowired(required = false)
private List restTemplates = Collections.emptyList(); //这里持有@LoadBalanced标记的RestTemplate实例
@Autowired(required = false)
private List transformers = Collections.emptyList();
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
final ObjectProvider
- > restTemplateCustomizers) {
return () -> restTemplateCustomizers.ifAvailable(customizers -> {
for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
for (RestTemplateCustomizer customizer : customizers) {
//为restTemplate添加定制
customizer.customize(restTemplate);
}
}
});
}
// ...
/**
* 以下针对classpath存在RetryTemplate.class的情况配置,先忽略
*/
@Configuration
@ConditionalOnClass(RetryTemplate.class)
public static class RetryAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public LoadBalancedRetryFactory loadBalancedRetryFactory() {
return new LoadBalancedRetryFactory() {
};
}
}
// ...
}
@LoadBalanced和@Autowried结合使用,意思就是这里注入的RestTempate Bean是所有加有@LoadBalanced注解标记的(持有@LoadBalanced标记的RestTemplate实例)
这段自动装配的代码的含义不难理解,就是利用了RestTempllate的拦截器,使用RestTemplateCustomizer对所有标注了@LoadBalanced的RestTemplate Bean添加了一个LoadBalancerInterceptor拦截器,而这个拦截器的作用就是对请求的URI进行转换获取到具体应该请求哪个服务实例ServiceInstance。
关键问下自己:为什么?
RestTemplate实例是怎么被收集的?
怎样通过负载均衡规则获取具体的具体的server?
继续扒看源码>
上面可以看出,会LoadBalancerAutoConfiguration类对我们加上@LoadBalanced注解的bean 添加loadBalancerInterceptor拦截器
LoadBalancerInterceptor#
Copy
/**
* 功能区的自动配置(客户端负载平衡)。
*/
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
private LoadBalancerClient loadBalancer;
private LoadBalancerRequestFactory requestFactory;
public LoadBalancerInterceptor(LoadBalancerClient loadBalancer,
LoadBalancerRequestFactory requestFactory) {
this.loadBalancer = loadBalancer;
this.requestFactory = requestFactory;
}
public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
// for backwards compatibility
this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
}
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
final URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
Assert.state(serviceName != null,
"Request URI does not contain a valid hostname: " + originalUri);
return this.loadBalancer.execute(serviceName,
this.requestFactory.createRequest(request, body, execution));
}
}
重点看intercept方法 当我们restTemplate执行请求操作时,就会被拦截器拦截进入intercept方法,而loadBalancer是LoadBalancerClient的具体实现
RibbonLoadBalancerClient#
Copy
public