4.1
Ribbon
要做什么事情?
先通过
"http://" + serviceId + "/info"
我们思考
ribbon
在真正调用之前需要做什么?
restTemplate.getForObject(“http://provider/info”, String.class);
想要把上面这个请求执行成功,我们需要以下几步
1.
拦截该请求;
2.
获取该请求的
URL
地址
:http://provider/info
3.
截取
URL
地址中的
provider
4.
从服务列表中找到
key
为
provider
的服务实例的集合
(
服务发现
)
5.
根据
负载均衡算法
选出一个符合的实例
6.
拿到该实例的
host
和
port
,重构原来
URL
中的
provider
7.
真正的发送
restTemplate.getForObject(“http://ip:port/info”
,
String.class)
4.2
Ribbon
负载均衡的测试
新增
controller
package com.it.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private LoadBalancerClient loadBalancerClient;
@GetMapping("testRibbon")
public String testRibbon(String serviceName){
//正常来讲,需要拿到ip和port 以及路径 才可以用
String url="http://"+serviceName+"/hello";
String forObject = restTemplate.getForObject(url, String.class);
return forObject;
}
/**
* 核心是负载均衡
* @param serviceName
* @return
*/
@GetMapping("testRibbonRule")
public String testRibbonRule(String serviceName){
ServiceInstance choose = loadBalancerClient.choose(serviceName);
return choose.toString();
}
}
访问:
http://localhost:8003/testChoose?serviceId=provider
4.3
从
choose
方法入手,查看
Ribbon
负载均衡的源码
走进 getServer()方法
在 chooseServer()里面得到 rule 是哪个对象
发现当前的 rule 是 ZoneAvoidanceRule 对象,而他只有一个父类 PredicateBasedRule
最终进入 PredicateBasedRule 类的 choose()方法
com.netflix.loadbalancer.AbstractServerPredicate#incrementAndGetModulo
4.4
负载均衡之前的服务列表是从何而来呢?
Ribbon
里面有没有服务列表?
Ribbon
只做负载均衡和远程调用
服务列表从哪来? 从
eureka
来
Ribbon
有一个核心接口
ILoadBalance
(承上
(eureka)
启下(Rule
))
我们发现在负载均衡之前,服务列表已经有数据了
重点接口 ILoadBalancer
重点接口 ILoadBalancer
Ribbon
没有服务发现的功能,但是
eureka
有,所以
ribbon
和
eureka
完美结合
首先关注这两个集合,就是存放从 eureka 服务端拉取的服务列表然后缓存到本地
我们去看
DynamicServerListLoadBalancer
类如何获取服务列表,然后放在
ribbon
的缓存里面
ServerList<T extends Server>
实现类(DiscoveryEnabledNIWSServerList
)
再回到 BaseLoadBalancer 中真正的存放服务列表
最后我们得知,只有在初始化
DynamicServerListLoadBalancer
类时,去做了服务拉取和缓存
也就是说并不是服务一启动就拉取了服务列表缓存起来,流程图如下
:
4.5
Ribbon
把
serverList
缓存起来,脏读怎么处理?
根据上面缓存服务列表我们得知,
ribbon
的每个客户端都会从
eureka-server
中把服务列表缓存起来 ,
主要的类是
BaseLoadBalancer
,那么有新的服务上线或者下线,这么保证缓存及时同步呢
Ribbon
中使用了一个
PING
机制
从
eureka
中拿到服务列表,缓存到本地,
ribbon
搞了个定时任务,隔一段时间就去循环
ping一下每个服务节点是否存活
我们查看 IPing 这个接口
我们就想看 NIWSDiscoveryPing
跟着 isAlive 一直往上找,看哪里去修改本地缓存列表
查看
notifyServerStatusChangeListener
发现只是一个空壳的接口,并没有对缓存的服务节点做出是实际操作,那么到底在哪里修改了缓存列表的值呢?
我们发现在
ribbon
的配置类中
RibbonClientConfiguration
有一个更新服务列表的方法
定时任务在哪里开始执行的呢?我们查找 doUpdate()方法
解决脏读机制的总结:
1. Ping
2.
更新机制
都是为了解决脏读的现象而生的
测试发现:更新机制和
ping
有个重回,而且在
ping
的时候不能运行更新机制,在更新的时
候不能运行
ping
机制,导致我们很难测到
ping
失败的现象!
Ping
机制做不了事情
4.6
Ribbon
负载均衡的实现和几种算法【重点】
在
ribbon
中有一个核心的负载均衡算法接口
IRule
1.RoundRobinRule--
轮询
请求次数
%
机器数量
2.RandomRule--
随机
3.
权重
4. iphash
3.AvailabilityFilteringRule --
会先过滤掉由于多次访问故障处于断路器跳闸状态的服务,还有并发的连接数量超过阈值的服务,然后对于剩余的服务列表按照轮询的策略进行访问
4.WeightedResponseTimeRule--
根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越大。刚启动时如果同统计信息不足,则使用轮询的策略,等统计信
息足够会切换到自身规则
5.RetryRule--
先按照轮询的策略获取服务,如果获取服务失败则在指定的时间内会进行重试,获取可用的服务
6.BestAvailableRule --
会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量小的服务
7.ZoneAvoidanceRule --
默认规则,复合判断
Server
所在区域的性能和
Server
的可用行选择服务器。
Ribbon
默认使用哪一个负载均衡算法:ZoneAvoidanceRule
:区间内亲和轮询的算法!通过一个
key
来区分
负载均衡算法:随机 轮训 权重
iphash(响应时间最短算法,区域内亲和(轮训)算法)