1.基本流程

Ribbon 要做什么事情?

先通过 “http://” + serviceId + “/info” 我们思考 ribbon 在真正调用之前需要做什么?

restTemplate.getForObject(“http://provider/info”, String.class);

想要把上面这个请求执行成功,我们需要以下几步

拦截该请求;

获取该请求的 URL 地址:http://provider/info

截取 URL 地址中的 provider

从服务列表中找到 key 为 provider 的服务实例的集合(服务发现(Eureka))

根据负载均衡算法选出一个符合的实例

拿到该实例的 host 和 port,重构原来 URL 中的 provider

真正的发送 restTemplate.getForObject(“http://ip:port/info”,String.class)

2.负载均衡测试

Ribbon源码分析_ribbon

Ribbon源码分析_ribbon_02

3.负载均衡流程 源码分析

Ribbon源码分析_ribbon_03

Ribbon源码分析_ribbon_04

LoadBalancer为负载均衡核心实现

Ribbon源码分析_ribbon_05

走进 getServer()方法

Ribbon源码分析_ribbon_06

在 chooseServer()里面得到 rule 是哪个对象

Ribbon源码分析_ribbon_07

当前的 rule 是 ZoneAvoidanceRule 对象,而他只有一个父类 PredicateBasedRule

Ribbon源码分析_ribbon_08

最终进入 PredicateBasedRule 类的 choose()方法

Ribbon源码分析_ribbon_09

Ribbon源码分析_ribbon_10

Ribbon源码分析_ribbon_11

4.负载均衡之前的服务列表

  • Ribbon 只做负载均衡和远程调用
  • 服务列表从哪来? 从 eureka 来
  • Ribbon 有一个核心接口 ILoadBalance(承上(eureka)启下(Rule))
  • 我们发现在负载均衡之前,服务列表已经有数据了


Ribbon源码分析_ribbon_12

Ribbon源码分析_ribbon_13

Ribbon源码分析_ribbon_14

Ribbon源码分析_ribbon_15

从 eureka 服务端拉取的服务列表然后缓存到本地

Ribbon源码分析_ribbon_16

DynamicServerListLoadBalancer 类如何获取服务列表,然后放在 ribbon 的缓存里面

Ribbon源码分析_ribbon_17

ServerList 实现类(DiscoveryEnabledNIWSServerList)

如果服务状态是up 则放入list集合中

Ribbon源码分析_ribbon_18

再回到 BaseLoadBalancer 中真正的存放服务列表

Ribbon源码分析_ribbon_19

Ribbon源码分析_ribbon_20


5.Ribbon缓存服务列表数据同步

根据上面缓存服务列表我们得知,ribbon 的每个客户端都会从 eureka-server 中把服务列表缓存起来

主要的类是 BaseLoadBalancer,那么有新的服务上线或者下线,这么保证缓存及时同步呢

Ribbon 中使用了一个 PING 机制,从 eureka 中拿到服务列表,缓存到本地,ribbon 搞了个定时任务,隔一段时间就去循环 ping一下每个服务节点是否存活。

6.Ribbon 负载均衡算法

1.RoundRobinRule–轮询 请求次数 % 机器数量

2.RandomRule–随机权重

3.AvailabilityFilteringRule --会先过滤掉由于多次访问故障处于断路器跳闸状态的服务,还有并发的连接数量超过阈值的服务,然后对于剩余的服务列表按照轮询的策略进行访问

4.WeightedResponseTimeRule–根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越大。刚启动时如果同统计信息不足,则使用轮询的策略,等统计信息足够会切换到自身规则

5.RetryRule-- 先按照轮询的策略获取服务,如果获取服务失败则在指定的时间内会进行重试,获取可用的服务

6.BestAvailableRule --会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量小的服务

7.ZoneAvoidanceRule – 默认规则,复合判断 Server 所在区域的性能和 Server 的可用行选择服务器。

Ribbon源码分析_ribbon_21

7.Ribbon初始化过程

ribbonClient的初始化

Ribbon源码分析_ribbon_22

Ribbon源码分析_ribbon_23

Ribbon源码分析_ribbon_24

●初始化构造过程:获取@LoadBalanced注解标记RestTemplate或者AsyncRestTemplate,然后添加拦截器。

● 负载均衡服务选择过程:在Ribbon设定的负载均衡策略下,从服务集群中根据预定的负载均衡策略实现后端服务的选取及请求转发。

Ribbon源码分析_ribbon_25

@LoadBalanced注解对RestTemplate做了标记,使用Ribbon的自动化配置加载类实现对负载均衡客户端的加载,在生成的RestTemplate的Bean上添加注解后,它会配置LoadBalancerClient

● ServiceInstance choose(String serviceId)方法,根据传入的serviceId(服务名),从负载均衡器中选择一个服务实例,服务实例通过ServiceInstance类来表示。

● execute方法,使用从负载均衡器中选择的服务实例来执行请求内容。

● URI reconstructURI ( ServiceInstance instance , URIoriginal ) 方 法  用 来 重 新 构 建 URI 。 我 们 通 过RestTemplate 请 求 后 端 服 务 时 会 使 用 serviceId ( 服 务名),这个方法会把请求的URI进行转换,返回host+port,再通过host+port的形式去请求服务。

Ribbon自动化加载机制

Ribbon源码分析_ribbon_26

Ribbon源码分析_ribbon_27

● 说 明 1 # : Ribbon 将 所 有 标 记 @LoadBalanced 注 解 的RestTemplate保存到一个List集合中。

● 说 明 2 # : Ribbon 借 助 了 Spring IoC 容 器 的SmartInitializingSingleton机制。实现该接口后,当所有单 例 Bean 都 被 初 始 化 完 成 后 , 容 器 会 调 用afterSingletonsInstantiated实现RestTemplateCustomizer的customize定制化方法。

● 说 明 3 # : 获 取 RestTemplate 的 interceptors , 在 构 造LoadBalancerInterceptor时需要传入LoadBalanceClient实例参数,LoadBalanceClient是一个接口,具体实现类将实现choose(服务实例选择)和execute(请求转发执行)方法,这一步完成Ribbon负载均衡策略Bean的构造。

● 说明4:将说明3#中构造的loadBalancerInterceptor Bean实例注入RestTemplate的定制化Bean中,这一步骤也会在说明2# 的 afterSingletonsInstantiated 方 法 中 被 调 用 , 完 成RestTemplate的定制化及与LoadBalancerInterceptor实例关联。

负载均衡

Ribbon源码分析_ribbon_28