在平時使用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
首先,在進行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);
}
複製代碼
以及攔截部分:將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));
}
複製代碼
至此,調用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有如下幾個。在大多數狀況下,這些默認的實現類是能夠知足需求的,若是有特性的需求,能夠本身實現。
那咱們就先看看RoundRobbinRule類中的輪詢策略:
①首先獲取全部存活的服務列表reachableServers及全部服務列表allServers,判斷兩個list是否爲空。 ②incrementAndGetModulo中則是對一個原子性變量進行+1操做,並同時進行一個CAS操做,去修改nextServerIndex值,保證輪詢的可靠性。可重試的輪詢策略以下:
可見此處多了兩行代碼:
long requestTime = System.currentTimeMillis();
long deadline = requestTime + maxRetryMillis;
複製代碼
定義了500ms的總重試時間,若是服務實例獲取不到,則進入循環,在循環中每次須要判斷一下是否超過總時間
while循環中,每次都會去獲取一下服務實例,而後進行判斷,若是實例仍然沒有獲取到,則對當前線程進行Thread.yield()操做,此操做的意義是:讓出當前線程時間分片,從新爭奪時間片,讓定時任務去執行,看是否達到規定時間,如到時間,則執行interrupt()操做,退出循環
這便是可重試的策略
Ribbon + RestTemplate 的負載平衡,流程是: 經過註解後,對url進行封裝request,攔截器對request進行攔截,而後根據負載均衡器去調用服務實例,完成負載平衡和服務調用