Ribbon
1.负载均衡场景
当我们的服务是一个集群的时候,客户端来访问集群,我们需要一个技术去选择集群中的某一个节点去响应客户端的请求,这个技术就是负载均衡。
2.负载均衡的分类
有两种分类:
(1)第一种:
服务端负载均衡:整体一个大的应用,此时需要一个中间件来选择集群某一个节点去响应。比如nginx,当请求过来的时候,由nginx负责去选择节点处理请求。
客户端负载均衡:微服务之间的调用,在调用方这里可以实现负载均衡,选取被调用方的某一个点来响应请求
(2)第二种:
集中式负载均衡:在服务的消费方和提供方之间使用独立的负载均衡设置,由该设施通过某种策略转发请求
客户端负载均衡:在消费方这里实现负载均衡,通过注册中心拉取可用的实现,自己通过某种算法选择一个合适的节点请求。
3.常见的负载均衡策略
随机:通过随机选择服务进行执行,一般这种方式使用较少;
轮询:负载均衡默认实现方式,请求来之后排队处理;
加权轮询:通过对服务器性能的分型,给高配置,低负载的服务器分配更高的权重,均衡各个
服务器的压力;
地址hash:通过客户端请求的地址的HASH值取模映射进行服务器调度。
最小连接数:即使请求均衡了,压力不一定会均衡,最小连接数法就是根据服务器的情况,比
如请求积压数等参数,将请求分配到当前压力最小的服务器上。
4.Ribbon的核心接口
(1)IClientConfig Ribbon的客户端配置,默认采用DefaultClientConfigImpl实现
(2)IRule:Ribbon的负载均衡策略,默认采用ZoneAvoidanceRule实现,该策略能够在多区域环境下选出 最佳区域的实例进行访问
(3)IPing: Ribbon的实例检查策略,默认采用DummyPing实现,该检查策略是一个特殊的实现,实际上
它并不会检查实例是否可用,而是始终返回true,默认认为所有服务实例都是可用的。
(4)ServerList:服务实例清单的维护机制,默认采用ConfigurationBasedServerList实现
(5)ServerListUpdater
(6)ILoadBalancer:负载均衡器,默认采用ZoneAwareLoadBalancer实现,它具备了区域感知的能力。
(6)ServerListFilter:服务实例清单过滤机制,默认采ZonePreferenceServerListFilter,该策略能够优先
过滤出与请求方处于同区域的服务实例。
5.1 IRule
我们看一下IRule接口的继承关系图
1)IRule常用的负载均衡实现类
(1)RandomRule:表示随机选择一个Server
(2)RoundRobinRule:轮询获取服务
(3)WeightResponseTimeRule:根据权重轮询获取服务,响应时间越快的服务权重越大被选中的几率也越大。
(4)RetryRule:先按照轮询策略获取服务,如果获取的服务失败则在指定的时间内进行重试,继续获取可用的服务。
(5)AvailabilityFilteringRule:过滤掉一直连接失败的被标记为circuit tripped的服务,并 过滤掉那些高并发的后端Server或者使用一个AvailabilityPredicate来包含过滤server的逻辑,其 实就是检查status里记录的各个Server的运行状态
(6)BestAvailableRule:选择一个最小的并发请求的Server,逐个考察Server,如果Server被 tripped了,则跳过
(7)ZoneAvoidanceRule:默认的负载均衡策略,即复合判断Server所在区域的性能和Server的可用性选择Server,没有区域的情况下,类似于轮询。
(8)NacosRule:一个集群内优先原则。
2)如何修改客户端的负载均衡策略
(1)方式一 全局配置
调用其他微服务的时候,都采用这一个策略
@Bean
public IRule changeIRule(){
//改为随机访问server
return new RandomRule();
}
(1)方式二 局部配置
在消费方的配置文件配置指定调用哪个微服务使用哪一种负载均衡策略
mall-order:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
3)自定义负载均衡策略
自定义负载均衡策略分为两大步
1.自定义规则
可以通过实现IRule接口或者继承AbstractLoadBalancerRule类来实现
我这里实现的是类似轮询的效果
public class MyRule extends AbstractLoadBalancerRule {
private AtomicInteger atomicInteger = new AtomicInteger(0);
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(),key);
}
public Server choose(ILoadBalancer loadBalancer,Object key){
if(loadBalancer==null){
return null;
}
//获取所有的服务
List<Server> allServers = loadBalancer.getReachableServers();//获得可用的服务。
int index = getAtomicInteger() % allServers.size();
return allServers.get(index);
}
//通过一个原子类,获取每次要访问的服务的索引
public final int getAtomicInteger(){
int current;
int next;
do{
current = this.atomicInteger.get();
next = current >=2147483647?0:current+1;
}while (!this.atomicInteger.compareAndSet(current,next));
System.out.println("这是第"+next+"次访问");
return next;
}
}
2.替换原本的负载均衡策略,这里由三种方式
方式一.通过一个javaConfig直接替换
@Bean
public IRule changeIRule(){
//改为随机访问server
return new MyRule();
}
方式二.在启动类上配置
这样指定的服务才使用这种负载均衡策略,前提是MyRule注入容器要放在主启动类的上一层包下,否则调用其他微服务也会使用这个负载均衡策略
@RibbonClient(name = "mall-order",configuration = MyRule.class)
方式三:在配置文件配置(推荐使用)
mall-order:
ribbon:
NFLoadBalancerRuleClassName: com.qianyue.mall.rule.MyRule
5.Ribbon的饥饿加载
在进行服务调用的时候,如果网络情况不好,第一次调用会超时。主要原因是Ribbon进行客户端负载均衡的Client并不是在服务启动的时候就初始化好的,而是在调用的时候才会去创建相应的Client,所以第一次调用的耗时不仅仅包含发送HTTP请求的时间,还包含了创建RibbonClient的时间,这样一来如果创建时间速度较慢,同时设置的超时时间又比较短的话,从而就会很容易发生请求超时的问题。
我们可用开启Ribbon的饥饿加载,在服务启动的时候就初始化好Client
ribbon:
eager-load:
enabled: true #开启饥饿加载
clients: mall-order #指定要调用的服务