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

 2019-11-23 11:07  阅读(914)
文章分类:Spring Cloud

阅读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,选中这个Interceptor20191123100213\_1.png

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

20191123100213\_2.png

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

此处有一个loadBalancer对象,查看到类型为:20191123100213\_3.png

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

20191123100213\_4.png

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

getLoadBalancer方法最后是通过spring上下文获取ILoadBalancer类型实例,默认为ZoneAwareLoadBalancer,书中有介绍,ILoadBalancer是在RibbonClientConfiguration中配置的。具体配置的代码片段:20191123100213\_5.png 我们继续执行getServer方法,不断深入,来到ZoneAvoidanceRule.choose方法,该方法在ZoneAvoidanceRule并未重写,所以执行的是父类型PredicateBasedRule的choose方法,方法具体如下:

20191123100213\_6.png

getAllServers方法获取到所有的服务列表,chooseRoundRobinAfterFiltering方法大致为筛选出可用的服务列表,以轮询的方式选择具体的server并封装到Optional中,最终返回server,至此我终于拿到了要访问server对象:20191123100213\_7.png

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

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


来源:http://ddrv.cn/a/88268

点赞(0)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> Spring Cloud源码分析:Ribbon如何为RestTemplate提供负载均衡

相关推荐