关联阅读(必读)
发送http请求(2):RestTemplate发送http请求
Springcloud源码阅读4-Ribbon负载均衡(下)
回顾
当我在Ribbon的环境下使用RestTemplate发送请求时,通常我们会像下面这样注入一个restTemplate
@Autowired @LoadBalanced RestTemplate restTemplate; 复制代码
为啥我们注入一个带有注解@LoadBalanced标注RestTemplate,此RestTemplate就具有负载均衡的能力,原理是什么呢?
在RestTemplate发送http请求一节讲过,RestTemplate可以添加拦截器,在发送请求前,先执行拦截器内容。
在Ribbon负载均衡(下)一节讲过,(LoadBalancerClient)RibbonLoadBalancerClient 具有负载均衡的能力。
猜测@LoadBalanced注解应该就是把这两点结合在了一起。
LoadBalancerAutoConfiguration配置类
这个整合剂,就是LoadBalancerAutoConfiguration配置类,
@Configuration @ConditionalOnClass(RestTemplate.class) @ConditionalOnBean(LoadBalancerClient.class) @EnableConfigurationProperties(LoadBalancerRetryProperties.class) public class LoadBalancerAutoConfiguration { //注入所有使用@LoadBalanced的RestTemplate @LoadBalanced @Autowired(required = false) private List<RestTemplate> restTemplates = Collections.emptyList(); // 对所有的RestTemplate使用RestTemplateCustomizer定制器进行统一处理 @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); } } }); } @Autowired(required = false) private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList(); //LoadBalancerRequest 创建工厂 @Bean @ConditionalOnMissingBean public LoadBalancerRequestFactory loadBalancerRequestFactory( LoadBalancerClient loadBalancerClient) { return new LoadBalancerRequestFactory(loadBalancerClient, transformers); } //LoadBalancerInterceptor 拦截器配置类 @Configuration @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate") static class LoadBalancerInterceptorConfig { // 负载均衡拦截器,看其参数传入的是loadBalancerClient @Bean public LoadBalancerInterceptor ribbonInterceptor( LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) { return new LoadBalancerInterceptor(loadBalancerClient, requestFactory); } // RestTemplate定制器 @Bean @ConditionalOnMissingBean public RestTemplateCustomizer restTemplateCustomizer( final LoadBalancerInterceptor loadBalancerInterceptor) { return restTemplate -> { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); }; } } //省略在有RetryTemplate类情况下的分析。 }
@LoadBalanced
@LoadBalanced注解的作用是啥呢?看起源码,我们发现他自身也有一个注解@Qualifier
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Qualifier public @interface LoadBalanced { }
@Qualifier 我们应该认识,当容器内存在多个同类型的Bean实现类时,我们可以通过@Qualifier("service1")注解来标识,具体引入哪个。
此处的@LoadBalanced 其实就是@Qualifier的一种特殊形态。 当我们使用@LoadBalanced 标注一个Bean定义时,在自动注入的地方,也使用@LoadBalanced 来自动注入对应的Bean
@LoadBalanced RestTemplate restTemplate(){ return new RestTemplate(); }
注入带有@LoadBalanced注解的RestTemplate
@LoadBalanced @Autowired(required = false) private List<RestTemplate> restTemplates = Collections.emptyList();
LoadBalancerInterceptor
负载均衡拦截器,有了这个拦截器,就可以设置到Restmplate上去,在请求发起之前做负载均衡操作,从多个候选应用中选择出一个。
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor { // LoadBalancerClient 负载均衡客户端 private LoadBalancerClient loadBalancer; // 用于构建出一个Request private LoadBalancerRequestFactory requestFactory; ... // 省略构造函数(给这两个属性赋值) @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)); } }
可以看出LoadBalancerInterceptor 会调用loadBalancer#execute()方法做负载均衡,获取一个服务地址出来。并传入一个回调。
LoadBalancerRequestFactory类 public LoadBalancerRequest<ClientHttpResponse> createRequest(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) { //回掉方法。 return instance -> { HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, loadBalancer); if (transformers != null) { for (LoadBalancerRequestTransformer transformer : transformers) { serviceRequest = transformer.transformRequest(serviceRequest, instance); } } return execution.execute(serviceRequest, body); }; }
当负载均衡选择出一个服务地址,apply回调方法,将当前HttpRequest 进行包装,重写其getURI()
@Override public URI getURI() { URI uri = this.loadBalancer.reconstructURI( this.instance, getRequest().getURI()); return uri; }
这样当真正发起请求时,获取的URI就是已经被负载均衡处理过的URI了。
RestTemplateCustomizer
RestTemplate定制器,用来对RestTemplate进行定制化操作,
此配置类注册的定制器,是用来对RestTemplate 设置LoadBalancerInterceptor的。
// RestTemplate定制器 @Bean @ConditionalOnMissingBean public RestTemplateCustomizer restTemplateCustomizer( final LoadBalancerInterceptor loadBalancerInterceptor) { return restTemplate -> { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); // 给RestTemplate 添加负载均衡拦截器 list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); }; }
当然我们可以配置其他的RestTemplateCustomizer 来对RestTemplate做其他定制化操作。
而这些RestTemplateCustomizer 定制器,定制操作。统一由配置类中注册的SmartInitializingSingleton触发。
SmartInitializingSingleton
实现该接口类,当所有单例 bean 都初始化完成以后, 容器会回调该接口的方法 afterSingletonsInstantiated。
此配置类中SmartInitializingSingleton 做的操作就是:对哪些RestTemplate,执行哪些定制化操作
// 对所有的RestTemplate使用RestTemplateCustomizer定制器进行统一处理 @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); } } }); }
也就说:spring在初始化过程后期,回调此配置类注册的SmartInitializingSingleton的afterSingletonsInstantiated,把上下文中的所有RestTemplateCustomizer 获取到,循环调用每个定制器的customize方法执行定制操作。其中就包括把LoadBalancerInterceptor设置到RestTemplate中。
至此:RestTemplate 就具有了负载均衡的能力。
总结
有@LoadBalanced 注解的RestTemplate,会被容器注入一个LoadBalancerInterceptor拦截器,从而使RestTemplate 具有负载均衡的能力。
讲完这节,Ribbon负载均衡与RestTemplate发送请求之间的知识点就形成了闭环。