前言
在上篇文章我们介绍了Ribbon
自带的负载均衡规则,以及如何切换使用自带的其它规则。那么可能有时候,自带规则满足不了我们的业务需求,此时就需要我们自己来自已定义规则,Ribbon
怎么来自定义规则呢?下面我们将来讲解。
自定义Ribbon规则
我们要自定义负载均衡规则,肯定是要有一个需求的,那么假设现在有一个需求就是每个服务要求调用次数达到3次,再切换到其它服务,Ribbon
自带的规则显然是满足不了我们的需求的,就需要我们自己来动手实现一个这样的规则。
在Ribbon
自带的规则里面,RandomRule
规则和我们这个需求是很相似的,我们就已这个规则为基础进行改造,加上调用次数的控制,我们先到GitHub
上找到RandomRule
的源码。
源码地址:https://github.com/Netflix/ribbon/tree/master/ribbon-loadbalancer/src/main/java/com/netflix/loadbalancer
找到RandomRule
这个类,点进去
ok,源码我们找到了,下面来新建一个类,叫做CustomRule
,来自定义我们的负载均衡规则。
此处大家注意一下,这个自定义配置类不能放在@ComponentScan所扫描的当前包下以及子包下, 否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,也就达不到特殊化定制的目的了。
官方说明:
大家应该都知道,SpringBoot项目的启动类是用@SpringBootApplication
注解来标示的,这个注解本身就已经包含了@ComponentScan
注解,如下图所示:
因此与启动类同级的包,以及子包都会被Spring所扫描的到,所以我们自定义的规则类,不能放在与启动类同一个包或子包下。需要新建一个包,此处新建了一个名为ribbonrule
的包:
然后将RandomRule
类的源码copy到新建的CustomRule
类里:
修改前的CustomRule
代码:
public class CustomRule extends AbstractLoadBalancerRule {
/**
* Randomly choose from all living servers
*/
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
Server server = null;
while (server == null) {
if (Thread.interrupted()) {
return null;
}
List<Server> upList = lb.getReachableServers();
List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
/*
* No servers. End regardless of pass, because subsequent passes
* only get more restrictive.
*/
return null;
}
int index = chooseRandomInt(serverCount);
server = upList.get(index);
if (server == null) {
/*
* The only time this should happen is if the server list were
* somehow trimmed. This is a transient condition. Retry after
* yielding.
*/
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
// Shouldn't actually happen.. but must be transient or a bug.
server = null;
Thread.yield();
}
return server;
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
}
修改后的CustomRule
代码:
public class CustomRule extends AbstractLoadBalancerRule {
private int total; // 当前服务总共被调用的次数
private int currentIndex; // 当前提供服务的下标
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
Server server = null;
while (server == null) {
if (Thread.interrupted()) {
return null;
}
List<Server> upList = lb.getReachableServers();
List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
/*
* No servers. End regardless of pass, because subsequent passes
* only get more restrictive.
*/
return null;
}
int index = chooseRandomInt(serverCount);
server = upList.get(index);
if (server == null) {
/*
* The only time this should happen is if the server list were
* somehow trimmed. This is a transient condition. Retry after
* yielding.
*/
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
// Shouldn't actually happen.. but must be transient or a bug.
server = null;
Thread.yield();
}
return server;
}
protected int chooseRandomInt(int serverCount) {
if (total < 3) {
total++;
return currentIndex;
} else {
total = 1; // 重置为1,切换服务也算调用一次
currentIndex++;
if(currentIndex == serverCount){
currentIndex = 0;
}
}
return currentIndex;
//return ThreadLocalRandom.current().nextInt(serverCount);
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
}
主要变动的地方就是chooseRandomInt
这个方法,这个方法传入的参数是服务总个数,修改前是随机返回的一个服务,修改后是定义了total
和currentIndex
这两个变量,默认都为0,分别来存储当前服务总共被调用的次数和当前提供服务的下标,如果total
未达到我们限定的值,那么就会直接返回currentIndex
,如果total
达到我们限定的值,那么就会重置total
,并且currentIndex++
,从而进行服务切换,当轮询到最后一个服务的时候,会重置currentIndex
,切换到下标为0的服务,从头开始。
接下来指定Ribbon
负载均衡规则为我们配置的规则,这个步骤与前面切换规则时一致,新建一个自定义规则配置类:
@Configuration
public class CustomRuleConfig {
// 指定ribbon负载均衡规则
@Bean
public IRule ribbonRule(){
return new CustomRule(); // 先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务
}
}
在启动类上加上@RibbonClient
注解,使自定义规则生效,name
就是微服务的名称,configuration
就是前面自定义规则配置类的class
,完整代码如下:
@SpringBootApplication
// 本服务启动后会自动注册进eureka服务中
@EnableEurekaClient
@RibbonClient(name = "USER-PROVIDER", configuration = CustomRuleConfig.class)
public class UserConsumer80_Application {
public static void main(String[] args) {
SpringApplication.run(UserConsumer80_Application.class,args);
}
}
上面都配置好后,就可以重启服务,查看效果了。