主流负载均衡方案
负载均衡:一般为集群模式下实现多服务的负载均衡,为服务降低压力,一般很多公司都会使用nginx实现负载均衡,负载均衡机制有多种。如轮询、随机、ip_hash等 我们可以实现自己的负载均衡算法进行扩展
Ribbon虽说已经闭源。但是负载均衡的机制也就是那么几套 在市面上也就这几种。就算有新的负载均衡器诞生也不会想着去换 除非说他的特别之处大于现负载均衡机制
我把服务方当做一个饭店 请求方当做客人
通过随机的方式去选择服务执行,这种方式一般使用的较少,打比方可能顾客比较随意 随机选择了一家饭店就进去了
通过轮询的方式去选择服务执行 这也是负载均衡默认的实现方式 其原理就是排队往下进行执行 打比方这位顾客很看好这一条街的饭店 想吃个遍 他就挨个门一个一个往下进
通过对服务器的性能分型。给高配置,低负载的服务器分配更高的权重,均衡服务器之间的压力,打比这家饭店的面积庞大 那么顾客量就可以承受更多 自然顾客就比旁边的小门面人多些 相对那么我们就可以给它加更大的权重
地址Hash就是通过客户端请求的地址的Hash值取模映射进行服务调度,一个ip进行hash行数就能得到一个数值进行取模 得到3个数值 根据不同的ip调度不同的服务
就是哪个服务里面的请求数最少 我就去哪个服务请求 不过注意不是说请求数均衡了 就压力也会均衡 最小连接数是根据服务器的情况 比如请求积压数等参数 将请求分配到压力最小的服务器当中 最小活跃数
在上篇文章中 我介绍了如何使用Nacos,那么细心的同学可以看到 其实Nacos中就已经引用了Ribbon,所以我们就不需要在去引用Ribbon的依赖了
Ribbon分为两种负载均衡策略,分别是全局和局部
全局:
全局既是在Configuration中设置全局,示例如下
package com.fujii.ribbon;
import com.alibaba.cloud.nacos.ribbon.NacosRule;
import com.netflix.loadbalancer.IRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Author: Fujii
* @Date: 2022-08-19 22:47
* @description: 负载均衡策略
*/
@Configuration
public class RibbonRandomRuleConfig {
@Bean
public IRule iRule(){
return new NacosRule();
}
}
局部:
局部既是在yml文件中设置局部的,既是给单独一个服务者提供负载均衡策略
stock-service:
ribbon:
NFLoadBalancerRuleClassName: com.fujii.ribbon.CustonRuleConfig
我们通过IRule接口可以看到它实现了多种负载均衡策略,下面我也会使用图中的规则来给大家做测试
random这个词大家应该都知道是随机的意思 ,这种策略就是随机选择一个服务实例。从以下源码图我们可以看到此类初始化了一个无参构造方法中创建了一个random对象,然后重写了chose()方法并且还调用了choose(ILoadBalancer lb, Object key)这个重载方法,每次利用random对象生成一个不大于服务总实例的随机数,将该数作为下标获取服务的一个实例
这个方法也就是轮询负载均衡,大家去打开此类的源码中可以发现choose(ILoadBalancer lb, Object key)方法首先是定义了一个计数器,然后while中循环遍历服务清单。在获取清单之间 会先根据incrementAndGetModulo()方法获取到当前的下标。该下标是不断的自增长先加上1然后和服务清单总数取模之后获取到的,所以此下标也不会出现下标越界的情况。然后拿着该下标去获取服务清单列表中的服务
每次循环一次,在循环10次还是没有获取到的情况下,就会报警"No available alive servers after 10 tries from load balancer: " + lb,ClientConfigEnabledRoundRobinRule这个类是实现了RoundRobinRule的方法,所以他选择的策略和RoundRobinRule的选择策略一致
该策略带有重试的功能,从下图可知RetryRule定义了一个subRule,它的实现类是RoundRobinRule,也就是说是在轮询的基础上加上了重试的机制
在choose(ILoadBalancer lb, Object key)方法中,每次还是根据RoundRobinRule的规则来选择服务,如果选择正常就返回,如果选择服务实例为null或者失效的时间之前不断的进行重试,重试的规则还是根据RoundRobinRule的规则来选择服务,如果超出了时间将会返回null
Weight这个使用过nginx的权重就肯定知道这个是设置权重的策略了,下面我们继续看源码图,从图中我们可以发现它是继承了RoundRobinRule,说明他是RoundRobinRule的一个子类。那么我们就可以想到肯定是在RoundRobinRule中进行了扩展。该类会给每一个实例的运行情况计算出该实例的权重,在挑选实例的基础上会根据权重进行挑选,这样可以实现更优的实例调用
该类中有一个DynamicServerWeightTask的方法实现定时任务,默认的情况下是每30秒会计算出一次多个服务实例当中的一个权重,如果一个服务中平均响应时间越短,说明被调用的概率也随之上升
当然大家在在之前的文章了解到Nacos也是可以给自己设置权重,那么Nacos的权重和这个WeightedResponseTimeRule是不同的,我们要实现Nacos的权限就要实现NacosRule来达到Nacos的权重,这是Nacos自己的一个扩展
BestAvailableRule是继承了ClientConfigEnabledRoundRobinRule,它在基础之上呢主要增加的就是根据loadBalancerStats中保存的服务实例状态信息来过滤失效的的服务实例的功能,然后找出并发最小的服务来调用,如果loadBalancerStats为null,则BestAvailableRule将采取父类的均衡策略
源码图中可见ZoneAvoidanceRule是继承PredicateBasedRule类,只不过是多了一个过滤的条件,它的过滤条件是以ZoneAvoidancePredicate为主过滤条件和以AvailabilityPredicate为次过滤条件组成的一个compositePredicate的组合过滤,过滤成功后继续采用线性轮询(RoundRuleRule)的方式从中选择一个
AvailabilityFilteringRule也是继承了PredicateBasedRule,说明就是在轮询的基础上进行扩展,这个策略的扩展内容就是过滤一些一直被连接失败的被标记的服务,并且过滤掉一些并发高的或者使用一个AbstractServerPredicate来包含过滤server的,就是在检查status中各个server的运行状态
我们也可以根据自己的实际需求来修改默认的负载均衡策略,从上面的源码解析得知我们所有的策略都是通过实现IRule接口来实现的,所以我们自己也可以通过实现这个接口来修改默认的配置,下面我在我的项目中来实战演练一下给大家做个展示
注意:在官方文档中介绍 配置类必须使用到@configuration注解 但是注意不能放入@CompenentScan或者main application上下文当中 如果放到里面会@RibbonClients或者@SpringBootApplication共享,所以我们的创建包的方式应该是如下的:
修改有两种方式来实现,第一种是配置类,第二种是配置文件的方式,我将会一一展示
配置类实现方式
编写一个配置类,返回随机数的负载均衡策略
package com.fujii.ribbon;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Author: Fujii
* @Date: 2022-08-20 0:33
* @description: 随机数负载均衡策略
*/
@Configuration
public class RandomRuleConfiguration {
@Bean
public IRule iRule(){
return new RandomRule();
}
}
然后 我们需要在启动类上使用@RibbonClients注解来引用配置
package com.fujii.order;
import com.fujii.ribbon.RandomRuleConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
/**
* @Author: Fujii
* @Date: 2022-08-16 22:07
* @description:
*/
@SpringBootApplication
@RibbonClients({
@RibbonClient(name = "stock-service",configuration = RandomRuleConfiguration.class)
})
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class,args);
}
}
以上的实现方式将会在启动服务之后随机获取 所以获取相同的服务多次也是正常的,下面我们在使用配置文件(application.yml)的方式来实现,实现之前记得把上面启动类中的@RibbonClients注解给注释掉 不然两种方式必定是会起冲突的
stock-service:
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule
上面这个stock-service是我的一个库存服务,下面我所调用的是NacosRule的负载均衡策略,我在Nacos中设置8022的端口为10 那么肯定我所调用的服务中 8022的几率要高于其他服务
其他的负载均衡策略大家可以换着去玩一玩,我这边就不一一介绍了
想自己自定义负载均衡策略那么就要继承AbstractLoadBalancerRule类,这个类呢是一个抽象类。里面主要定义了一个ILoadBalancer,定义的目的就在于辅助负载均衡策略选取合适的服务端实例
这个配置类就不需要像上面一样规定性的放哪儿了,你放在ComponentScan当中也没事儿。
package com.fujii.order.config;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import org.springframework.context.annotation.Configuration;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
/**
* @Author: Fujii
* @Date: 2022-08-20 1:15
* @description:
*/
@Configuration
public class CustomRuleConfiguration extends AbstractLoadBalancerRule {
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object o) {
ILoadBalancer loadBalancer = this.getLoadBalancer();
//获取当前请求服务的实例
List<Server> reachableServers = loadBalancer.getReachableServers();
int random = ThreadLocalRandom.current().nextInt(reachableServers.size());
Server server = reachableServers.get(random);
return server;
}
}
上面这个只是实现了最基本的随机负载均衡策略的功能,更多的扩展可根据自身需求进行扩展,然后下一步在配置文件中将上面这个配置类的路径copy到配置文件中
stock-service:
ribbon:
NFLoadBalancerRuleClassName: com.fujii.order.config.CustomRuleConfiguration
我们发现每次调用服务的时候第一次调用才会去加载负载均衡策略,如果网络不好的情况下可能会直接超时,那么我们可以开启饥饿加载来解决这个问题,就是说在启动时就会帮我们打开饥饿加载,并不是在运行时通过我们的第一次请求才去加载,具体配置如下
ribbon:
eager-load:
#开启ribbon饥饿加载
enabled: true
#服务实例,多个直接用逗号分隔
clients: stock-service
好了,这期就到这里!
结束语
若要前行,就要离开你现在停留的地方!