@LoadBalanced注解RestTemplate拥有负载均衡的能力

关联阅读(必读)

发送http请求(1):发送http请求的几种方式java

发送http请求(2):RestTemplate发送http请求spring

Springcloud源码阅读4-Ribbon负载均衡(下)app

回顾

当我在Ribbon的环境下使用RestTemplate发送请求时,一般咱们会像下面这样注入一个restTemplate负载均衡

@Autowired
@LoadBalanced
RestTemplate restTemplate;
复制代码

为啥咱们注入一个带有注解@LoadBalanced标注RestTemplate,此RestTemplate就具备负载均衡的能力,原理是什么呢?ide

RestTemplate发送http请求一节讲过,RestTemplate能够添加拦截器,在发送请求前,先执行拦截器内容。函数

Ribbon负载均衡(下)一节讲过,(LoadBalancerClient)RibbonLoadBalancerClient 具备负载均衡的能力。post

猜想@LoadBalanced注解应该就是把这两点结合在了一块儿。ui

LoadBalancerAutoConfiguration配置类

这个整合剂,就是LoadBalancerAutoConfiguration配置类,this

@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注解的做用是啥呢?看起源码,咱们发现他自身也有一个注解@Qualifierspa

@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发送请求之间的知识点就造成了闭环。

推荐阅读:

SpringCloud源码阅读0-SpringCloud必备知识

SpringCloud源码阅读1-EurekaServer源码的秘密

SpringCloud源码阅读2-Eureka客户端的秘密

SpringCloud源码阅读3-Ribbon负载均衡(上)

Springcloud源码阅读4-Ribbon负载均衡(下)

欢迎你们关注个人公众号【源码行动】,最新我的理解及时奉送。