程序员社区

SpringCloud构建微服务之Ribbon自定义负载均衡规则

前言

在上篇文章我们介绍了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这个方法,这个方法传入的参数是服务总个数,修改前是随机返回的一个服务,修改后是定义了totalcurrentIndex这两个变量,默认都为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);
    }
}

上面都配置好后,就可以重启服务,查看效果了。

赞(0) 打赏
未经允许不得转载:IDEA激活码 » SpringCloud构建微服务之Ribbon自定义负载均衡规则

一个分享Java & Python知识的社区