[TOC]
之前咱们介绍了治理分布式组件注册的服务;eureka、consul、zookeeper、nacos他们都能够实现咱们服务的注册于获取。
然而理论咱们还是须要咱们本人调用最终的客户端获取数据的。
前提概要
- 下面的服务发现框架都能够应用。consul因为须要保障网络通信失常。而eureka是咱们本人注册java服务。所以这里就抉择通过eureka来作为咱们的服务治理框架。
- 这里咱们就借助咱们之前eureka服务搭建整合文章。咱们间接用之前那个分支启动eureka服务,一个order服务、两个payment服务。
- 而后还是拜访咱们
localhost/order/payment/123
这个接看看响应是否是负载平衡的。
- 这些都是咱们eureka章节的内容。这个时候问题来了。为什么restTemplate会实现负载平衡。这里咱们查阅材料就会发现在服务治理框架中会注入ribbon框架。在ribbon注册的时候回将ribbon提供的拦截器注入到restTemplate中。restTemplate执行之前会先走拦截器从而实现负载平衡。
- 所以重点还是在ribbon。因为是他实现了负载平衡
Ribbon作用
- ribbon是springcloud我的项目组件。全名spring-cloud-ribbon。他的次要性能是负载平衡和服务调用。ribbon在服务调用是有超时,重试的设置。外部提供默认负载平衡机制。也提供接口不便咱们自定义负载平衡策略。
- ribbon的服务调用借助月RestTemplate,RestTemplate的负载平衡依赖于ribbon。两者是相辅相成的一个产品。
Ribbon原理
- 下面也说了适宜RestTemplate联合应用的。还有springcloud提供的Feign联合应用的。Ribbon首先外部在构建一个http包下的
ClientHttpRequestInterceptor
拦截器。
@Beanpublic LoadBalancerInterceptor ribbonInterceptor( LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) { return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);}
- 而后会获取所有被
LoadBalanced
规范的RestTemplate。遍历所有RestTemplate诶个注入咱们LoadBalancerInterceptor拦截器。在这个拦截器外部会实现服务列表获取。而后负载平衡。
Ribbon源码剖析
- 原理很简略。就是在RestTemplate调用之前根据Ribbon的能力获取真正须要调用的地址而后交由RestTemplate调用。
Ribbon主动配置
- 基于springboot的spi机制咱们可能发现在springcloud-common中会加载
LoadBalancerAutoConfiguration
配置类。通过名称咱们大略也能理解到这个类是配置负载平衡的主动配置类。 - 上面咱们来看看这个类都为咱们筹备了哪些工作。
- 首先是注入两个成员变量。restTeplates、transformers两个。
- restTemplates就是获取所有被@LoadBalanced标注的restTemplate。筹备为他们注入拦截器
- LoadBalancerRequestTransformer类正文
Allows applications to transform the load-balanced {@link HttpRequest} given the chosen
- LoadBalancerRequestTransformer类正文意思就是容许给指定的申请切换负载平衡策略。这里能力无限有工夫在深挖一下。
LoadBalanced
- 这里咱们须要先介绍下Loadbanced这个注解。为什么退出这个注解咱们就能获取到指定的RestTemplate汇合呢。
- 点开源码发现也没啥货色,就是一个注解示意。然而这个注解不个别。咱们留神到他外部有个元注解@Qualifier。这个注解是
org.springframework.beans.factory.annotation
包下。理解Spring的读者应该晓得这是spring注入类的一种形式。对于spring注入形式解析咱们独自开篇剖析下。这里咱们只用记住@Qualifier会注入雷同属性的bean. - 什么叫雷同属性的bean。比方咱们下面可能会多个中央注入RestTemplate。增加@Loadbanlanced注解相当于如下注解
- 而后@Qualifier联合@Autowired注解就会注入所有RestTemplate在spring容器中的bean。且@Qualifier中的value=""的。也就是下面两个注解spring就会收罗到负载平衡标记的RestTemplate
LoadBalancerInterceptor
- 索罗到对象之后上面理所应到应该开始筹备拦截器了
- 下面生成RestTemplateCustomizer对象springcloud是通过lamda表达式生成的。实际上就是实现RestTemplateCustomizer这个接口。外部会将RestTemplate对象调用set办法将LoadBancerInterptor拦截器注入到对象内。在RestTemplate执行的时候回先通过过滤器的洗礼。这里留个坑吧。对于RestTemplate调用咱们稍后再说。
回到LoadBalancerAutoConfiguration
- 咱们持续回到LoadBalancerAutoConfiguration . 下面咱们晓得两个注入的属性的作用了。在前面咱们看到了
SmartinitializingSingleton
和LoadBalancerRequestFactory
。对于LoadbalancerRequestFactory
这理论就是个工厂。在结构LoadBalancerIntercrptor
拦截器的时候须要用到。 - 重点在
SmartInitializingSingleton
这个类。外面传了一个参数通过ObjectProvider封装的。ObjectProvider
的作用能够简略了解为@Autowired
。而外部的RestTemplateCustomer
就是咱们上文提到的作用是为了封装RestTemplate
。 customizer.customize
就是调用增加拦截器了。- 到此RestTemplate机会被装在有Ribbon实现的拦截器了。当咱们在通过RestTemplate调用接口的时候就会有负载平衡的性能了。
RetryTemplate
- 主动配置之后咱们发现还有两个配置类。认真看看和下面的配置是一样的。然而多了retry字眼。这个类次要依赖于RetryTemplate。是用来重试的。
- 同样是为RestTemplate注入interceptor。只不过都是Retry模式的。
RetryLoadBalancerInterceptor
拦挡外部实现里实际上就是RetryTemplate和RestTemplate的区别。
- 两个拦截器的区别其实就是前者多了一个重试机制。具体重试的策略是通过
LoadBalancedRetryPolicy
设置的。 - 在Ribbon中RetryTemplate是须要内部提供的。因为咱们零碎中没有退出所以这里爆红。这里也就没有生成重试的成果。有趣味的读者能够试试。
RestTemplate联合Ribbon调用原理
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor
- 咱们间接察看
LoadBalancerInterceptor
这个拦截器不难发现。他就是实现了ClientHttpRequestIntrceptor
。这里咱们记住这个接口。在RestTemplate调用的时候必定会设计到ClientHttpRequestInterceptor
这个类。
RestTemplate源码跟踪
- 咱们还是拿咱们的order订单举例。还记的咱们的订单拜访接口吗
http://localhost/order/getpayment/123
。
getForObject
@Override@Nullablepublic <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);}
- RestTemplate办法外部首先通过申请头验证并组装response。最终会执行execute办法。
execute
- execute外部是doExecute.下面是doExecute办法。大略逻辑也很简略。
①、构建request对象
②、发送申请
③、解决响应
④、返回响应数据 - 咱们的重点在构建request对象上。很显著是在createRequest里进行拦截器设置的。最终在request.execute执行中进行拦截器链路执行的。
- RestTemplate的类构造就是集成HttpAccessor。外部会保护ClientHttpRequestFactory来构建request对象。
- 最终会在
InterceptingClientHttpRequestFactory
这个类中的createRequest办法上。
-外部就是属性的赋值了。
- 下面request.execute最终就会落在InterceptingClientHttpRequestFactory.execute 这个能够跟踪下这个类的接口就晓得办法入口了。
- 在下面咱们可能看到会先判断是否有拦截器,有的话会间接交由拦截器执行。从代码中咱们也可能看出
InterceptingClientHttpRequest
对象会在拦截器局部阻塞。所以这里咱们不难看出下面Ribbon实现的LoadBalancerInterceptor。这个拦截器外部必定须要实现接口的调度。
- 咱们在LoadBalancerInteptor拦截器中看到后面会Ribbon找寻地址。而后交由InterceptorHttpAccess中内置的InterceptorClientHttpRequestFactory工厂来解决申请。没错这个类就是咱们下面申请的货色。货色有回去。这个createRequest办法咱们下面曾经看过了,如果外部存在拦截器就会交由拦截器实现。如果没有就会进行转发
- 这里InterceptorClientHttpRequestFactory有点责任链模式的感觉。
Ribbon负载平衡源码追踪
- 下面别离介绍了Ribbon主动配置、RestTemplate联合Ribbon发送申请两局部源码追踪。当初咱们再把锋芒指向最终RIbbon的看家本领负载平衡的源码吧。
- 下面咱们源码追踪可能晓得在调用之前是通过LoadBalancerClient.execute办法实现负载平衡的。LoadBalancerClient在那个模块注册到spring容器里我临时没有找到。心愿理解的读者能够指出。多谢!!!!!
public interface LoadBalancerClient extends ServiceInstanceChooser { <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException; <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException; URI reconstructURI(ServiceInstance instance, URI original);}
- 最终LoadBalancerClient外部是通过
ILoadBalancer
来实现负载平衡的。说实话笔者这里为了省事并没有落实ILoadBalancer
和LoadBalancerClient
之间是如何绑定的。 - 咱们能够看
BaseLoadBalancer
对ILoadBalancer
的实现。外面重要的是chooseServer
这个办法。
public Server chooseServer(Object key) { if (counter == null) { counter = createCounter(); } counter.increment(); if (rule == null) { return null; } else { try { return rule.choose(key); } catch (Exception e) { logger.warn("LoadBalancer [{}]: Error choosing server for key {}", name, key, e); return null; } }}
- 咱们能够看到外面有个Counter对象用于治理数量。最初会有Irule对象去实现负载策略。
RoundRobbinRule
中就是通过AtomicInteger原子类操作申请次数在于容器数量取模决定获取哪个容器进行调用的。
public Server choose(ILoadBalancer lb, Object key) { if (lb == null) { log.warn("no load balancer"); return null; } Server server = null; int count = 0; while (server == null && count++ < 10) { List<Server> reachableServers = lb.getReachableServers(); List<Server> allServers = lb.getAllServers(); int upCount = reachableServers.size(); int serverCount = allServers.size(); if ((upCount == 0) || (serverCount == 0)) { log.warn("No up servers available from load balancer: " + lb); return null; } int nextServerIndex = incrementAndGetModulo(serverCount); server = allServers.get(nextServerIndex); if (server == null) { /* Transient. */ Thread.yield(); continue; } if (server.isAlive() && (server.isReadyToServe())) { return (server); } // Next. server = null; } if (count >= 10) { log.warn("No available alive servers after 10 tries from load balancer: " + lb); } return server;}
总结
-Ribbon为咱们提供了很多中内置的负载平衡策略。罕用的就是轮询。如果上述的不满足咱们也能够通过实现Irule来自定义策略规定。
- 只须要在启动类上通过
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MySelfRule.class)
来指定咱们的服务下负载策略。