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《Spring Cloud源码分析:Ribbon如何为RestTemplate提供负载均衡》

没错,是他就是他,集合中神奇的被注入了一个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方法:

《Spring Cloud源码分析:Ribbon如何为RestTemplate提供负载均衡》

可以看到此处通过request对象获取到了请求的服务名称serviceName,此处获取到的是product。(发现贴入的源码没有debug时的变量信息,不太直观,后面还是贴图好了。)

此处有一个loadBalancer对象,查看到类型为:《Spring Cloud源码分析:Ribbon如何为RestTemplate提供负载均衡》

前面有提到过,LoadBalancerClient是为RestTemplate提供负载均衡的接口,此处使用的是Ribbon方式的实现类。继续进入execute方法:

《Spring Cloud源码分析:Ribbon如何为RestTemplate提供负载均衡》

此处有一个getLoadBalancer方法,该方法返回值为ILoadBalancer接口类型,其实这个类型才是Ribbon真正的负载均衡器。

getLoadBalancer方法最后是通过spring上下文获取ILoadBalancer类型实例,默认为ZoneAwareLoadBalancer,书中有介绍,ILoadBalancer是在RibbonClientConfiguration中配置的。具体配置的代码片段:《Spring Cloud源码分析:Ribbon如何为RestTemplate提供负载均衡》 我们继续执行getServer方法,不断深入,来到ZoneAvoidanceRule.choose方法,该方法在ZoneAvoidanceRule并未重写,所以执行的是父类型PredicateBasedRule的choose方法,方法具体如下:

《Spring Cloud源码分析:Ribbon如何为RestTemplate提供负载均衡》

getAllServers方法获取到所有的服务列表,chooseRoundRobinAfterFiltering方法大致为筛选出可用的服务列表,以轮询的方式选择具体的server并封装到Optional中,最终返回server,至此我终于拿到了要访问server对象:《Spring Cloud源码分析:Ribbon如何为RestTemplate提供负载均衡》

product启动了俩个实例,mac:8888、mac:9999,此次获取获取到的是mac:9999,再讲server封装成RibbonServer,最终执行execute方法向获取到的server对象发起具体请求。

至此我发现Ribbon为RestTemplate提供负载均衡的切入点就在InterceptingClientHttpRequest类型,但是为什么RestTemplate的Interceptor集合中会有该类型的实例呢?答案在书中也有说明,都是在LoadBalancerAutoConfiguration配置类型中生成的。具体可以查看源码。

    原文作者:Spring Cloud
    原文地址: https://blog.csdn.net/qq_22606825/article/details/83749697
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞