Spring Cloud原始碼分析Ribbon
在之前介紹使用Ribbon進行服務消費的時候,我們用到了RestTemplate
,但是熟悉Spring的同學們是否產生過這樣的疑問:RestTemplate
不是Spring自己就有的嗎?跟Ribbon的客戶端負載均衡又有什麼關係呢?下面在本文,我們來看RestTemplate
和Ribbon
是如何聯絡起來並實現客戶端負載均衡的。
首先,回顧一下之前的消費者示例:我們是如何實現客戶端負載均衡的?仔細觀察一下程式碼之前的程式碼,我們可以發現在消費者的例子中,可能就是這個註解@LoadBalanced
是之前沒有接觸過的,並且從命名上來看也與負載均衡相關。我們不妨以此為線索來看看原始碼實現的機制。
從@LoadBalanced
註解原始碼的註釋中,我們可以知道該註解用來給RestTemplate
標記,以使用負載均衡的客戶端(LoadBalancerClient
)來配置它。
通過搜尋LoadBalancerClient
,我們可以發現這是Spring
Cloud中定義的一個介面:
123456789 |
public interface LoadBalancerClient { ServiceInstance choose(String serviceId); <T> T execute(String serviceId, LoadBalancerRequest<T> request) |
從該介面中,我們可以通過定義的抽象方法來了解到客戶端負載均衡器中應具備的幾種能力:
-
ServiceInstance choose(String serviceId)
:根據傳入的服務名serviceId
,從負載均衡器中挑選一個對應服務的例項。 -
T execute(String serviceId, LoadBalancerRequest request) throws IOException
-
URI reconstructURI(ServiceInstance instance, URI original)
:為系統構建一個合適的“host:port”形式的URI。在分散式系統中,我們使用邏輯上的服務名稱作為host來構建URI(替代服務例項的“host:port”形式)進行請求,比如:http://myservice/path/to/service
。在該操作的定義中,前者ServiceInstance
物件是帶有host和port的具體服務例項,而後者URI物件則是使用邏輯服務名定義為host的URI,而返回的URI內容則是通過ServiceInstance
的服務例項詳情拼接出的具體“host:post”形式的請求地址。
順著LoadBalancerClient
介面的所屬包org.springframework.cloud.client.loadbalancer
,我們對其內容進行整理,可以得出如下圖的關係:
從類的命名上我們初步判斷LoadBalancerAutoConfiguration
為實現客戶端負載均衡器的自動化配置類。通過檢視原始碼,我們可以驗證這一點假設:
從類的命名上我們初步判斷LoadBalancerAutoConfiguration
為實現客戶端負載均衡器的自動化配置類。通過檢視原始碼,我們可以驗證這一點假設:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546 |
public class LoadBalancerAutoConfiguration { (required = false) private List<RestTemplate> restTemplates = Collections.emptyList(); public SmartInitializingSingleton loadBalancedRestTemplateInitializer( final List<RestTemplateCustomizer> customizers) { return new SmartInitializingSingleton() { public void afterSingletonsInstantiated() { for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) { for (RestTemplateCustomizer customizer : customizers) { customizer.customize(restTemplate); } } } }; } public RestTemplateCustomizer restTemplateCustomizer( final LoadBalancerInterceptor loadBalancerInterceptor) { return new RestTemplateCustomizer() { public void customize(RestTemplate restTemplate) { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); } }; } public LoadBalancerInterceptor ribbonInterceptor( LoadBalancerClient loadBalancerClient) { return new LoadBalancerInterceptor(loadBalancerClient); }}(RestTemplate.class) (LoadBalancerClient.class) |
從LoadBalancerAutoConfiguration
類頭上的註解可以知道Ribbon實現的負載均衡自動化配置需要滿足下面兩個條件:
-
@ConditionalOnClass(RestTemplate.class)
:RestTemplate
類必須存在於當前工程的環境中。 -
@ConditionalOnBean(LoadBalancerClient.class)
:在Spring的Bean工程中有必須有LoadBalancerClient
的實現Bean。
在該自動化配置類中,主要做了下面三件事:
-
建立了一個
LoadBalancerInterceptor
的Bean,用於實現對客戶端發起請求時進行攔截,以實現客戶端負載均衡。 -
建立了一個
RestTemplateCustomizer
的Bean,用於給RestTemplate
增加LoadBalancerInterceptor
攔截器。 -
維護了一個被
@LoadBalanced
註解修飾的RestTemplate
物件列表,並在這裡進行初始化,通過呼叫RestTemplateCustomizer
的例項來給需要客戶端負載均衡的RestTemplate
增加LoadBalancerInterceptor
攔截器。
接下來,我們看看LoadBalancerInterceptor
攔截器是如何將一個普通的RestTemplate
變成客戶端負載均衡的:
123456789101112131415161718192021222324252627282930313233343536373839404142 |