Spring Cloud源碼分析:Ribbon如何爲RestTemplate提供負載均衡

閱讀Spring Cloud微服務實戰中的Ribbon源碼分析章節,debug跟蹤源碼後,做此記錄。

從@LoadBalanced註解源碼的註釋中可以知道, 該註解用來給RestTemplate做標記, 以使用負載均衡的客戶端(LoadBalancerClient)來配置它。

在讀完這個章節後,還是沒能理解Ribbon是怎樣爲RestTemplate提供負載均衡。於是debug進行源碼跟蹤,纔算瞭解了大致流程。

從下面這段代碼開始,發起一個GET請求,目標服務爲product,product服務實例有倆個。

 @GetMapping("hello")
    public String hello(String name){
        String result = restTemplate.getForObject("http://product/hello?name={1}", String.class, name);
        return result;
    }

進入getForObject方法,不斷深入,忽略掉不重要的參數封裝、處理,到下面這段源碼:

protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,
			ResponseExtractor<T> responseExtractor) throws RestClientException {

		Assert.notNull(url, "'url' must not be null");
		Assert.notNull(method, "'method' must not be null");
		ClientHttpResponse response = null;
		try {
			ClientHttpRequest request = createRequest(url, method);
			if (requestCallback != null) {
				requestCallback.doWithRequest(request);
			}
			response = request.execute();
			handleResponse(url, method, response);
			if (responseExtractor != null) {
				return responseExtractor.extractData(response);
			}
			else {
				return null;
			}
		}
		catch (IOException ex) {
			...
		}
		finally {
			...
		}
	}

可以很明顯的看到 request.execute() ,猜一下也能知道這裏要開始真正發起請求了。進入execute方法,執行對象爲AbstractClientHttpRequest類型,繼續進入executeInternal方法,執行對象爲AbstractBufferingClientHttpRequest類型,繼續進入executeInternal方法,執行對象爲AbstractBufferingClientHttpRequest的子類型InterceptingClientHttpRequest,敲黑板,劃重點。十分驚奇的發現該類型中封裝了一個ClientHttpRequestInterceptor的集合,在書中的介紹有說明Ribbon爲RestTemplate的負載均衡提供了一個LoadBalancerInterceptor,並且LoadBalancerInterceptor是直接實現自ClientHttpRequestInterceptor,選中這個Interceptor

沒錯,是他就是他,集合中神奇的被注入了一個LoadBalancerInterceptor實例,那接下來的關注點就是LoadBalancerInterceptor是如何實現負載均衡的。

回到之前追蹤到的位置:

public ClientHttpResponse execute(HttpRequest request, final byte[] body) throws IOException {
			if (this.iterator.hasNext()) {
				ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
				return nextInterceptor.intercept(request, body, this);
			}
			else {
                ...
			}
		}

很明顯,此處開始調用Interceptor進行攔截邏輯處理了。進入intercept方法:

可以看到此處通過request對象獲取到了請求的服務名稱serviceName,此處獲取到的是product。(發現貼入的源碼沒有debug時的變量信息,不太直觀,後面還是貼圖好了。)

此處有一個loadBalancer對象,查看到類型爲:

前面有提到過,LoadBalancerClient是爲RestTemplate提供負載均衡的接口,此處使用的是Ribbon方式的實現類。繼續進入execute方法:

此處有一個getLoadBalancer方法,該方法返回值爲ILoadBalancer接口類型,其實這個類型纔是Ribbon真正的負載均衡器。

getLoadBalancer方法最後是通過spring上下文獲取ILoadBalancer類型實例,默認爲ZoneAwareLoadBalancer,書中有介紹,ILoadBalancer是在RibbonClientConfiguration中配置的。具體配置的代碼片段: 我們繼續執行getServer方法,不斷深入,來到ZoneAvoidanceRule.choose方法,該方法在ZoneAvoidanceRule並未重寫,所以執行的是父類型PredicateBasedRule的choose方法,方法具體如下:

getAllServers方法獲取到所有的服務列表,chooseRoundRobinAfterFiltering方法大致爲篩選出可用的服務列表,以輪詢的方式選擇具體的server並封裝到Optional中,最終返回server,至此我終於拿到了要訪問server對象:

product啓動了倆個實例,mac:8888、mac:9999,此次獲取獲取到的是mac:9999,再講server封裝成RibbonServer,最終執行execute方法向獲取到的server對象發起具體請求。

至此我發現Ribbon爲RestTemplate提供負載均衡的切入點就在InterceptingClientHttpRequest類型,但是爲什麼RestTemplate的Interceptor集合中會有該類型的實例呢?答案在書中也有說明,都是在LoadBalancerAutoConfiguration配置類型中生成的。具體可以查看源碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章