SpringCloud Ribbon源碼探索學習

Ribbon使用

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

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

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

同時去使用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後,到底作了什麼dom

1、封裝

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

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

2、攔截

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

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

3、根據負載均衡器調用服務實例

至此,調用RibbonLoadBalancerClient中的execute方法,先查看一下RibbonLoadBalancerClient類:this

其中LoadBalancerClient接口,有以下三個方法,其中excute()爲執行請求,reconstructURI()用來重構url。url

ServiceInstanceChooser接口,主要有一個方法,用來根據serviceId來獲取ServiceInstance。spa

它繼承了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進行攔截,而後根據負載均衡器去調用服務實例,完成負載平衡和服務調用

相關文章
相關標籤/搜索