LoadBalancerClient 是 SpringCloud 提供的一种负载均衡客户端,Ribbon 负载均衡组件内部也是集成了 LoadBalancerClient 来实现负载均衡。那么 LoadBalancerClient 内部到底是如何做到的呢?我们先大概讲一下 LoadBalancerClient 的实现原理,然后再深入源码中进行解析。

LoadBalancerClient 在初始化时会通过 Eureka Client 向 Eureka 服务端获取所有服务实例的注册信息并缓存在本地,并且每10秒向 EurekaClient 发送 “ping”,来判断服务的可用性。如果服务的可用性发生了改变或者服务数量和之前的不一致,则更新或者重新拉取。最后,在得到服务注册列表信息后,ILoadBalancer 根据 IRule 的策略进行负载均衡(默认策略为轮询)。

当使用 LoadBalancerClient 进行远程调用的负载均衡时,LoadBalancerClient 先通过目标服务名在本地服务注册清单中获取服务提供方的某一个实例,比如订单服务需要访问商品服务,商品服务有3个节点,LoadBalancerClient 会通过 choose() 方法获取到3个节点中的一个服务,拿到服务的信息之后取出服务IP信息,就可以得到完整的想要访问的IP地址和接口,最后通过 RestTempate 访问商品服务。

深入解析 LoadBalancerClient 接口源码:

1、LoadBalancerClient 源码解析:

LoadBalancerClient 是 Spring Cloud 提供的一个非常重要的接口,它继承自 ServiceInstanceChooser 接口,该接口的实现类是 RibbonLoadBalanceClient,它们之间的关系如下图所示:

(1)LoadBalancerClient 接口源码:

首先我们开始追踪 LoadBalancerClient 源码:

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 接口继承了 ServiceInstanceChooser 接口,主要的方法为2个 execute() 方法,均是用来执行请求的。还有个 reconstructURI() 是用来重构URL的。

(2)ServiceInstanceChooser 接口源码:

继续查看 LoadBalancerClient 继承的 ServiceInstanceChooser 接口源码,具体如下:

public interface ServiceInstanceChooser
{ServiceInstance choose(String serviceId);
}

ServiceInstanceChooser 接口中的主要方法为 choose(),该方法用于根据服务的名称 serviceId 来选择其中一个服务实例,即根据 serviceId 获取ServiceInstance。

(3)RibbonLoadBalanceClient 实现类源码:

接下来我们看看 LoadBalancerClient 的实现类 RibbonLoadBalanceClient,它用来执行最终的负载均衡请求。其中,RibbonLoadBalanceClient 的一个 choose() 方法用于选择具体的服务实例,其内部是通过 getServer() 方法交给 ILoadBalancer 完成的。我们先看下 RibbonLoadBalanceClient 里面几个重要实现方法的源码:

① 第一个:choose(),用来选择具体的服务实例。

 @Overridepublic ServiceInstance choose(String serviceId) {return choose(serviceId, null);}/*** New: Select a server using a 'key'.* @param serviceId of the service to choose an instance for* @param hint to specify the service instance* @return the selected {@link ServiceInstance}*/public ServiceInstance choose(String serviceId, Object hint) {Server server = getServer(getLoadBalancer(serviceId), hint);if (server == null) {return null;}return new RibbonServer(serviceId, server, isSecure(server, serviceId),serverIntrospector(serviceId).getMetadata(server));}

② 第二个:getServer(),获取实例。

 protected Server getServer(ILoadBalancer loadBalancer) {return getServer(loadBalancer, null);}protected Server getServer(ILoadBalancer loadBalancer, Object hint) {if (loadBalancer == null) {return null;}// 最终通过 loadBalancer 去做服务实例的选择。// Use 'default' on a null hint, or just pass it on?return loadBalancer.chooseServer(hint != null ? hint : "default");}

可以看到最终通过 loadBalancer 去做服务实例的选择。那我们下面就看下 loadBalancer 是怎么怎么实现服务实例的选择的?

(4)BaseLoadBalancer 源码:

我们从上面能看到 ILoadBalancer 中的 chooseServer 方法里面默认值为 default,进入ILoadBalancer 实现类 BaseLoadBalancer 的 chooseServer() 看下:

我们的 key 的值为“default”,那么这个 key 代表的是什么意思呢?进去 rule 对象里面看下:

可以看到这个 rule 是 IRule 接口声明出来的,且默认定义的实现类是 RoundRobinRule(),也就是轮询策略。那我们接下来看下 IRule 接口:

(5)IRule 接口源码:

public interface IRule{/** choose one alive server from lb.allServers or* lb.upServers according to key* * @return choosen Server object. NULL is returned if none*  server is available */public Server choose(Object key);public void setLoadBalancer(ILoadBalancer lb);public ILoadBalancer getLoadBalancer();
}

我们可以看到 IRule 接口定义了3个方法,choose() 是用来选择实例的,setLoadBalancer() 和 getLoadBalance() 用来设置和获取 ILoadBalancer 的。那么接下来 IRule 接口有多少个实现类:

(1)随机策略 RandomRule:随机数选择服务列表中的服务节点Server,如果当前节点不可用,则进入下一轮随机策略,直到选到可用服务节点为止

(2)轮询策略 RoundRobinRule:按照接收的请求顺序,逐一分配到不同的后端服务器

(3)重试策略 RetryRule:在选定的负载均衡策略机上重试机制,在一个配置时间段内当选择Server不成功,则一直尝试使用 subRule 的方式选择一个可用的server;

(4)可用过滤策略 PredicateBaseRule:过滤掉连接失败 和 高并发连接 的服务节点,然后从健康的服务节点中以线性轮询的方式选出一个节点返回

(5)响应时间权重策略 WeightedRespinseTimeRule:根据服务器的响应时间分配一个权重weight,响应时间越长,weight越小,被选中的可能性越低。主要通过后台线程定期地从 status 里面读取平均响应时间,为每个 server 计算一个 weight

(6)并发量最小可用策略 BestAvailableRule:选择一个并发量最小的服务节点 server。ServerStats 的 activeRequestCount 属性记录了 server 的并发量,轮询所有的server,选择其中 activeRequestCount 最小的那个server,就是并发量最小的服务节点。该策略的优点是可以充分考虑每台服务节点的负载,把请求打到负载压力最小的服务节点上。但是缺点是需要轮询所有的服务节点,如果集群数量太大,那么就会比较耗时。

(7)区域权重策略 ZoneAvoidanceRule:综合判断 server 所在区域的性能 和 server 的可用性,使用 ZoneAvoidancePredicate 和 AvailabilityPredicate 来判断是否选择某个server,前一个判断判定一个zone的运行性能是否可用,剔除不可用的zone(的所有server),AvailabilityPredicate 用于过滤掉连接数过多的Server。

同样的,如果我们也可以通过实现 IRule 接口来自定义一个负载均衡策略。

2、ILoadBalancer 源码解析:

ILoadBalancer 是一个接口,该接口定义了一系列实现负载均衡的方法,LoadBalancerClient 的实现类 RibbonLoadBalanceClient 也将负载均衡的具体实现交给了 ILoadBalancer 来处理,ILoadBalancer 通过配置 IRule、IPing 等,向 EurekaClient 获取注册列表信息,默认每10秒向 EurekaClient 发送一次 “ping”,进而检查是否需要更新服务的注册列表信息。最后,在得到服务注册列表信息后,ILoadBalancer 根据 IRule 的策略进行负载均衡。ILoadBalancer 接口的实现类结果如下图所示:

查看 BaseLoadBalancer 和 DynamicServerListLoadBalancer 源码,默认情况下实现了以下配置:

(1)IClientConfig clientConfig:用于配置负载均衡客户端,默认实现类是 DefaultClientConfigImpl。

(2)IRule rule:用于配置负载均衡的策略,默认使用的是 RoundRobinRule 轮询策略。

(3)IPing ping:用于检查当前服务是否有响应,从而判断当前服务是否可用,默认实现类是 DummyPing,该实现类的 isAlive() 方法返回值是 true,默认所有服务实例都是可用的。

(4)ServerList serverList:用于获取所有 Server 注册列表信息。通过跟踪源码会发现,ServerList 的实现类是 DiscoveryEnabledNIWSServerList,该类定义的 obtainServersViaDiscovery() 方法是根据 eurekaClientProvider.get() 方法获取 EurekaClient,再根据 EurekaClient 获取服务注册列表信息。EurekaClient 的实现类是DiscoveryClient,DiscoveryClient 具有服务注册、获取服务注册列表等功能。

(5)ServerListFilter filter:定义了根据配置过滤或者动态获取符合条件的服务列表,默认实现类是 ZonePreferenceServerListFilter,该策略能够优先过滤出与请求调用方处于同区域的服务实例。

SpringCloud LoadBalancerClient 负载均衡原理相关推荐

  1. Ribbon负载均衡原理,Feign是如何整合Ribbon的?

    文章目录 1. 什么是负载均衡? 2. Ribbon的使用 ①:自定义负载均衡策略 ②:Ribbon的饥饿加载 3. Ribbon的负载均衡原理 ①:收集带有@LoadBalanced注解的RestT ...

  2. php负载均衡原理_Java开发大型互联网架构深入负载均衡原理之方案分析

    引言 负载均衡 建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽.增加吞吐量.加强网络数据处理能力.提高网络的灵活性和可用性. 负载均衡,英文名称为Load Balan ...

  3. Docker Swarm服务发现和负载均衡原理

    Docker Swarm服务发现和负载均衡原理 Docker使用的是Linux内核iptables和IPVS的功能来实现服务发现和负载均衡.Iptables是Linux内核中可用的包过滤技术,可根据数 ...

  4. 搞懂分布式技术8:负载均衡原理剖析

    负载均衡的重要性无需多说,今天带来:负载均衡原理的解析. 开头先理解一下所谓的"均衡". 不能狭义地理解为分配给所有实际服务器一样多的工作量,因为多台服务器的承载能力各不相同,这可 ...

  5. 使用LVS实现负载均衡原理及安装配置详解

    使用LVS实现负载均衡原理及安装配置详解 负载均衡集群是 load balance 集群的简写,翻译成中文就是负载均衡集群.常用的负载均衡开源软件有nginx.lvs.haproxy,商业的硬件负载均 ...

  6. ngnix 负载均衡原理

    ngnix 负载均衡原理

  7. 负载均衡原理与实践详解 第三篇 服务器负载均衡的基本概念-网络基础

    负载均衡原理与实践详解 第三篇 服务器负载均衡的基本概念-网络基础 系列文章: 负载均衡详解第一篇:负载均衡的需求 负载均衡详解第二篇:服务器负载均衡的基本概念-网络基础 负载均衡详解第三篇:服务器负 ...

  8. 使用Zookeeper实现负载均衡原理

    思路 使用Zookeeper实现负载均衡原理,服务器端将启动的服务注册到,zk注册中心上,采用临时节点.客户端从zk节点上获取最新服务节点信息,本地使用负载均衡算法,随机分配服务器. 创建项目工程 M ...

  9. 负载均衡原理与实践详解 第五篇 负载均衡时数据包流程详解

    负载均衡原理与实践详解 第五篇 负载均衡时数据包流程详解 系列文章: 负载均衡详解第一篇:负载均衡的需求 负载均衡详解第二篇:服务器负载均衡的基本概念-网络基础 负载均衡详解第三篇:服务器负载均衡的基 ...

最新文章

  1. 设计模式6——创建型模式之原型模式
  2. content-type的作用
  3. 基于Tomcat5.0和Axis2开发Web Service应用实例
  4. cocos2d-x调度器原理,mainloop的Update
  5. 速成pytorch学习——6天Dataset和DataLoader
  6. Calendar导入java,Java程序使用Calendar.add()方法将分钟添加到当前时间
  7. 全球最大手机公司又发年度新品:AI亮得一笔,智能音箱又晚又丑
  8. Hibernate 一对多连接表单向关联
  9. 如何防止三分钟热度?给自己的目标定个阶段性奖励吧
  10. aio-pika的使用
  11. php中seo优化怎么做,SEO技术:对于PHP页面的SEO优化
  12. java导出乱码_导出文件乱码问题处理(java)
  13. oracle grid infrastructure 安装,安装Oracle Grid Infrastructure for a Standalone Server
  14. 刚从阿里、头条面试回来,java字符串截取后四位
  15. 谷粒商城项目8——商品上架 上架商品sku保存到es nginx配置
  16. 一直搞不懂SIMNOW网站的逻辑
  17. Unpacker ExeCryptor 2.x.x. version 1.0 RC1 [Public Build]
  18. 2022年7月软件著作权登记证书改革后版权局那边现在就是要求高质量发展,有源代码材料的只有积极配合版权工作,才好顺利下证了
  19. 科妹家的员工表彰会来了!超燃!
  20. 51Nod 1584 加权约数和

热门文章

  1. 六十五、下一个更大的数系列,单调栈解决方法
  2. 三十八、学Sql,不了解Sql注入怎么行?
  3. tornado设置cookie和seesion
  4. 梦世界服务器修改指令,我的世界梦世界有哪些指令必须知道 梦世界所有必须知道指令汇总...
  5. 从多篇2021年顶会论文看多模态预训练模型最新研究进展
  6. 边界化难题终结者!将自监督学习应用到自动驾驶上 | CVPR 2021
  7. NLP多任务学习:一种层次增长的神经网络结构 | PaperDaily #16
  8. 计算机视觉:基于眼疾分类数据集iChallenge-PM图像分类经典模型剖析(LeNet,AlexNet,VGG,GoogLeNet,ResNet)
  9. python的前端框架_web前端三大主流框架之Python异步框架如何工作?
  10. 注册域名需要资格吗_考教师资格证需要居住证吗?