@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負載均衡(下)

歡迎你們關注個人公衆號【源碼行動】,最新我的理解及時奉送。

相關文章
相關標籤/搜索