1. 程式人生 > >SpringCloud Ribbon原始碼探索學習

SpringCloud Ribbon原始碼探索學習

Ribbon使用

在平時使用Ribbon時,更多的是將Ribbon與RestTemplate相結合:

	@Bean
	@LoadBalanced
	RestTemplate restTemplate(){
		return new RestTemplate();
	}
複製程式碼

首先定義一個RestTemplate,通過註解注入,同時註解也完成了負載均衡。

同時去使用restTemplate進行Rest呼叫

    @Override
    public String hiService(String name){
        return restTemplate.getForObject("http://SERVER-HI/hi?name="
+name,String.class); } 複製程式碼

那麼在我們在輸入http://localhost:8770/hi?name=lixin後,到底做了什麼

一、封裝

首先,在進行getForObject方法後,會將帶入的url進行封裝,封裝成http請求request,然後被攔截器LoadBalancerInterceptor進行攔截,程式碼分別是:

@Override
	@Nullable
	public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
		RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
		HttpMessageConverterExtractor<T> responseExtractor =
				new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
		return
execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); } 複製程式碼

二、攔截

以及攔截部分:將request攔截下,擷取其中URL及服務名,用於之後呼叫負載均衡方法時,選擇合適的服務例項

	@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)); } 複製程式碼

三、根據負載均衡器呼叫服務例項

至此,呼叫RibbonLoadBalancerClient中的execute方法,先檢視一下RibbonLoadBalancerClient類:

其中LoadBalancerClient介面,有如下三個方法,其中excute()為執行請求,reconstructURI()用來重構url。

ServiceInstanceChooser介面,主要有一個方法,用來根據serviceId來獲取ServiceInstance。

它繼承了ServiceInstanceChooser及LoadBalancerClient類,最終的負載均衡的請求處理,由它來執行

首先,去獲取需要載入的負載均衡策略,通過getLoadBalancer方法執行,預設是輪詢RoundRobbinRule方式:

在獲取到負載均衡策略之後,通過getServer()方法去獲取例項,點選進入getServer方法,發現是ILoadBalancer類去選擇服務例項。

在ILoadBalancer介面中,addServers()方法是新增一個Server集合;chooseServer()方法是根據key去獲取Server;markServerDown()方法用來標記某個服務下線;getReachableServers()獲取可用的Server集合;getAllServers()獲取所有的Server集合。

chooseServer則是由BaseLoadBalancer類進行實現:

根據程式碼可以看出,具體choose方法是根據不同的負載均衡策略,會有不同的選擇方法,返回具體根據策略得到的服務例項。

最後根據服務例項,進行請求的呼叫。

負載均衡策略

IRule用於複雜均衡的策略,它有三個方法,其中choose()是根據key 來獲取server,setLoadBalancer()和getLoadBalancer()是用來設定和獲取ILoadBalancer的

IRule有很多預設的實現類,這些實現類根據不同的演算法和邏輯來處理負載均衡。Ribbon實現的IRule有以下幾個。在大多數情況下,這些預設的實現類是可以滿足需求的,如果有特性的需求,可以自己實現。

  • BestAvailableRule 選擇最小請求數
  • ClientConfigEnabledRoundRobinRule 輪詢
  • RandomRule 隨機選擇一個server
  • RoundRobinRule 輪詢選擇server
  • RetryRule 根據輪詢的方式重試
  • WeightedResponseTimeRule 根據響應時間去分配一個weight ,weight越低,被選擇的可能性就越低
  • ZoneAvoidanceRule 根據server的zone區域和可用性來輪詢選擇

RoundRobbinRule

那我們就先看看RoundRobbinRule類中的輪詢策略:

①首先獲取所有存活的服務列表reachableServers及所有服務列表allServers,判斷兩個list是否為空。 ②incrementAndGetModulo中則是對一個原子性變數進行+1操作,並同時進行一個CAS操作,去修改nextServerIndex值,保證輪詢的可靠性。
③最後判斷服務是否可用,如果不可用,則重新進入迴圈。
④如果在10次迴圈後,仍然沒有可用的服務,則退出迴圈並進行警告。最後返回服務例項

RetryRule

可重試的輪詢策略如下:

可見此處多了兩行程式碼:

long requestTime = System.currentTimeMillis();
long deadline = requestTime + maxRetryMillis;
複製程式碼

定義了500ms的總重試時間,如果服務例項獲取不到,則進入迴圈,在迴圈中每次需要判斷一下是否超過總時間

if判斷中,嵌套了一個定時任務,去判斷是否達到預定時間,如果達到了,則對當前執行緒進行interrupt()操作

while迴圈中,每次都會去獲取一下服務例項,然後進行判斷,如果例項仍然沒有獲取到,則對當前執行緒進行Thread.yield()操作,此操作的意義是:讓出當前執行緒時間分片,重新爭奪時間片,讓定時任務去執行,看是否達到規定時間,如到時間,則執行interrupt()操作,退出迴圈

這即是可重試的策略

總體流程

Ribbon + RestTemplate 的負載平衡,流程是: 通過註解後,對url進行封裝request,攔截器對request進行攔截,然後根據負載均衡器去呼叫服務例項,完成負載平衡和服務呼叫