發送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配置類,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註解的做用是啥呢?看起源碼,咱們發現他自身也有一個註解@Qualifier
spa
@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();
複製代碼
負載均衡攔截器,有了這個攔截器,就能夠設置到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了。
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觸發。
實現該接口類,當全部單例 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源碼閱讀3-Ribbon負載均衡(上)
Springcloud源碼閱讀4-Ribbon負載均衡(下)
歡迎你們關注個人公衆號【源碼行動】,最新我的理解及時奉送。