Spring Cloud教程 第三彈 Ribbon工作原理

胖波發表於2020-11-07

 

寫在前面的話

本文只介紹Ribbon的工作原理,關於Ribbon的其它教程請看這篇文章 Spring Cloud教程 第二彈 客戶端負載均衡Ribbon

因為Ribbon的工作原理我寫的有點細,篇幅稍大,所以單獨抽出來了,沒有將本文糅合在第二彈裡

 

1、揭開RestTemplate的神祕面紗

我們實際上關心的是,為什麼@LoadBalanced註解能賦予RestTemplate負載均衡的能力?

關於Ribbon的核心工作原理,請小夥伴們一定要一步一步耐心往下看。

 

首先,Ribbon元件在啟動時,會自動載入RibbonAutoConfiguration這個配置類,如下圖所示

可以看到,RibbonAutoConfiguration載入於EurekaClientAutoConfiguration之前,載入於LoadBalancerAutoConfiguration之後

EurekaClientAutoConfiguration不必多說,引入Eureka Client必然會載入這個類

而RibbonAutoConfiguration中宣告瞭這樣的一個Bean,如下圖所示

 

RibbonLoadBalancerClient主要為載入LoadBalancerAutoConfiguration服務,沒有RibbonLoadBalancerClient這個Bean,後者無法載入,具體細節看下圖。

 

下面主要就來看看LoadBalancerAutoConfiguration這個類做了什麼事情

首先,LoadBalancerAutoConfiguration有這樣一塊程式碼:

這塊程式碼表示的含義是:將所有用@LoadBalanced註解標識的RestTemplate型別的Bean注入到List集合中,而恰好我們的確也宣告瞭這樣的一個Bean,如下圖所示:


 
這不就是我們很熟悉的那塊程式碼嘛

@LoadBalanced註解與@Qualifier註解有關,關於@LoadBalanced註解的原理請看這篇文章 Ribbon中@LoadBalanced註解的原理

 

另外,LoadBalancerAutoConfiguration類中還有另外幾塊重要的程式碼,為了保持整潔,不重要的我就沒有貼上出來了,如下所示。

public class LoadBalancerAutoConfiguration {
    @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);
                }
            }
        });
	}


	@Bean
	@ConditionalOnMissingBean
	public LoadBalancerRequestFactory loadBalancerRequestFactory(
			LoadBalancerClient loadBalancerClient) {
		return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
	static class LoadBalancerInterceptorConfig {

		@Bean
		public LoadBalancerInterceptor ribbonInterceptor(
				LoadBalancerClient loadBalancerClient,
				LoadBalancerRequestFactory requestFactory) {
			return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
		}

		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final LoadBalancerInterceptor loadBalancerInterceptor) {
			return restTemplate -> {
				List<ClientHttpRequestInterceptor> list = new ArrayList<>(
						restTemplate.getInterceptors());
				list.add(loadBalancerInterceptor);
				restTemplate.setInterceptors(list);
			};
		}

	}

}

 

1、loadBalancerRequestFactory方法用於宣告一個負載均衡請求生成工廠

2、LoadBalancerInterceptorConfig 這個內部靜態類宣告瞭兩個Bean:

          2.1、ribbonInterceptor該方法宣告瞭一個負載均衡攔截器的Bean,該方法有兩個引數loadBalancerClient、LoadBalancerRequestFactory requestFactory,這兩個引數都已經有了。而LoadBalancerInterceptor繼承自ClientHttpRequestInterceptor,表明LoadBalancerInterceptor就是spring boot的一個攔截器

          2.2、restTemplateCustomizer該方法用於宣告一個RestTemplateCustomizer型別的Bean。該bean的目的在於:將RestTemplate與LoadBalancerInterceptor繫結起來,這是RestTemplate具有負載均衡能力的關鍵

3、loadBalancedRestTemplateInitializerDeprecated方法用於觸發RestTemplateCustomizer這個Bean的執行

 

綜上所述,RestTemplate發出的請求最後是被LoadBalancerInterceptor這個攔截器攔截到了,下面看一下LoadBalancerInterceptor的intercept方法主要做了哪些事情:

@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, requestFactory.createRequest(request, body, execution));
	}

最終的請求交由loadBalancer這個負載均衡器執行,this.loadBalancer的型別是LoadBalancerClient,而LoadBalancerClient的實現為RibbonLoadBalancerClient,LoadBalancerClient繼承自ServiceInstanceChooser介面

public interface ServiceInstanceChooser {
    ServiceInstance choose(String serviceId);
}

通過this.loadBalancer.execute這行程式碼往裡面跟蹤,找到最終的執行方法如下(execute-1會呼叫execute-2):

 

getServer方法的實現細節:

ILoadBalancer的chooseServer方法會轉換為呼叫IRule的choose方法,這與我在前面所說的形成了前呼後應

request.apply方法的實現細節:

request物件是在LoadBalancerInterceptor的intercept方法中傳過來的,具體如下圖:

 

點進去看看,如下圖:

 

最後的execution.execute方法是執行最終的http請求的地方,至此,一次ribbon的生命週期就到此結束了。

 

總結

關於Ribbon的工作原理,我寫的雖然有點多,但是很詳細,耐心看完的話基本沒有啥疑問的,該懂的都會懂。當你讀懂之後,發現Ribbon似乎不再神祕了

 

 

相關文章