代码详解七种负载均衡策略(长文多图)

本文深入探讨了DUBBO中的负载均衡策略,包括简单随机、加权随机、简单轮询、简单加权轮询、平滑加权轮询、一致性哈希和最少活跃数策略。通过设计思路、代码实例和DUBBO源码分析,详细阐述了每种策略的工作原理和实现方式,帮助读者理解如何在分布式环境中有效地分配请求。
摘要由CSDN通过智能技术生成

 1 三组概念

负载均衡、集群容错、服务降级这三个概念在DUBBO中非常重要,同理其它分布式框架也都有相同或者相近之概念。

从调用顺序角度分析,调用顺序依次是负载均衡、集群容错、服务降级。从解决问题角度分析,负载均衡解决了「选哪一个」问题,集群容错解决了「换哪一个」问题,服务降级解决了「全错怎么办」问题。

假设有1个服务消费者面对10个提供者,这时面临第一个问题就是「选哪一个」进行调用,所以负载均衡最先调用,假设选定了5号服务提供者进行服务调用。

假设消费者调用5号提供者发生了超时异常,这时面临第二个问题就是「换哪一个」进行调用:5号超时要不要换1号试一试,或者直接返回不进行重试,所以集群容错第二个调用。

假设已经重试了1号、3号、6号提供者全部超时,这时面临「全错怎么办」这第三个问题,这时可以直接返回一个固定值或者提示文案,所以服务降级第三个调用。

负载均衡作为整个调用链路第一个节点非常重要,本文结合DUBBO源码分析以下七种负载均衡策略:

  • 简单随机
  • 加权随机
  • 简单轮询
  • 简单加权轮询
  • 平滑加权轮询
  • 一致性哈希
  • 最少活跃数

2 简单随机

简单随机含义是服务消费者每次会任意访问一个服务提供者,并且从概率角度看每个提供者被访问概率一致,可以通过指定范围随机数实现。第一步编写服务器代码

public class MyServer {

    private String ip;

    public MyServer(String ip) {
        this.ip = ip;
    }

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }
}
复制代码

第二步编写基础负载均衡策略,其它策略可以复用

public abstract class AbstractLoadBalance {

    public MyServer select(List<MyServer> serverList) {
        return doSelect(serverList);
    }

    public abstract MyServer doSelect(List<MyServer> serverList);
}
复制代码

第三步编写简单随机策略

public class RandomBalance extends AbstractLoadBalance {

    @Override
    public MyServer doSelect(List<MyServer> serverList) {
        // 随机数范围[0,serverListSize)
        int index = ThreadLocalRandom.current().nextInt(serverList.size());
        return serverList.get(index);
    }
}
复制代码

第四步编写测试代码

public class LoadBalanceTest {

    public static void main(String[] args) {
        List<MyServer> serverList = buildData();
        testRandomBalance(serverList);
    }

    public static void testRandomBalance(List<MyServer> serverList) {
        AbstractLoadBalance randomBalance = new RandomBalance();
        for (int i = 0; i < 10; i++) {
            MyServer server = randomBalance.select(serverList);
            System.out.println("RandomBalance route server=" + server);
        }
    }

    public static List<MyServer> buildData() {
        List<MyServer> serverList = new ArrayList<MyServer>();
        MyServer server1 = new MyServer("192.1.1.1");
        MyServer server2 = new MyServer("192.1.1.2");
        MyServer server3 = new MyServer("192.1.1.3");
        serverList.add(server1);
        serverList.add(server2);
        serverList.add(server3);
        return serverList;
    }
}
复制代码

第五步输出结果,循环次数越多结果越准确

RandomBalance route server=MyServer(ip=192.1.1.2)
RandomBalance route server=MyServer(ip=192.1.1.1)
RandomBalance route server=MyServer(ip=192.1.1.3)
RandomBalance route server=MyServer(ip=192.1.1.2)
RandomBalance route server=MyServer(ip=192.1.1.1)
RandomBalance route server=MyServer(ip=192.1.1.1)
RandomBalance route server=MyServer(ip=192.1.1.2)
RandomBalance route server=MyServer(ip=192.1.1.2)
RandomBalance route server=MyServer(ip=192.1.1.3)
RandomBalance route server=MyServer(ip=192.1.1.3)
复制代码

3 加权随机

3.1 设计思路

加权随机新增了权重这个概念,假设服务器A权重等于1,服务器B权重等于5,从概率角度看B服务器被访问概率5倍于A服务器。实现按照权重访问有很多种方式,我们选择使用概率区间这个思路。

假设现在有三台服务器,服务器权重分别是3、5、2,那么三者构成如下图概率区间:

概率区间计算步骤如下图:

3.2 代码实例

第一步编写服务器代码

public class MyServer {

    private String ip;

    private int weight;

    public MyServer(String ip) {
        this.ip = ip;
    }

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }
}
复制代码

第二步编写加权随机策略

public class RandomWeightBalance extends AbstractLoadBalance {

    @Override
    public MyServer doSelect(List<MyServer> serverList) {
        // 所有服务器总权重
        int totalWeight = 0;
        // 第一个服务器权重
        int firstWeight = serverList.get(0).getWeight();
        // 所有服务器权重相等
        boolean sameWeight = true;
        // 遍历所有服务器
        for (MyServer server : serverList) {
            // 计算总权重
            totalWeight += server.getWeight();
            // 任意一个invoker权重不等于第一个权重则设置sameWeight=false
            if (sameWeight && server.getWeight() != firstWeight) {
                sameWeight = false;
            }
        }
        // 权重不相等则根据权重选择
        if (!sameWeight) {
            // 在总区间范围[0,totalWeight)生成随机数A
            Integer offset = ThreadLocalRandom.current().nextInt(totalWeight);
            // 遍历所有服务器区间
            for (MyServer server : serverList) {
                // 如果A在server区间直接返回
                if (offset < server.getWeight()) {
                    return server;
                }
                // 如果A不在server区间则减去此区间范围并继续匹配其它区间
                offset -= server.getWeight();
            }
        }
        // 所有服务器权重相等则随机选择
        return serverList.get(ThreadLocalRandom.current().nextInt(serverList.size()));
    }
}
复制代码

第三步编写测试代码

public class LoadBalanceTest {

    public static void main(String[] args) {
        List<MyServer> serverList = buildData();
        testRandomWeightBalance(serverList);
    }

    public static void testRandomWeightBalance(List<MyServer> serverList) {
        AbstractLoadBalance randomBalance = new RandomWeightBalance();
        for (int i = 0; i < 10; i++) {
            MyServer server = randomBalance.select(serverList);
            System.out.println("RandomWeightBalance route server=" + server);
        }
    }

    public static List<MyServer> buildData() {
        List<MyServer> serverList = new ArrayList<MyServer>();
        MyServer server1 = new MyServer("192.1.1.1", 3);
        MyServer server2 = new MyServer("192.1.1.2", 5);
        MyServer server3 = new MyServer("192.1.1.3", 2);
        serverList.add(server1);
        serverList.add(server2);
        serverList.add(server3);
        return serverList;
    }
}
复制代码

第四步输出结果,循环次数越多结果越准确

RandomWeightBalance route server=MyServer(ip=192.1.1.2, weight=2)
RandomWeightBalance route server=MyServer(ip=192.1.1.2, weight=3)
RandomWeightBalance route server=MyServer(ip=192.1.1.1, weight=3)
RandomWeightBalance route server=MyServer(ip=192.1.1.1, weight=3)
RandomWeightBalance route server=MyServer(ip=192.1.1.3, weight=2)
RandomWeightBalance route server=MyServer(ip=192.1.1.3, weight=2)
RandomWeightBalance route server=MyServer(ip=192.1.1.2, weight=5)
RandomWeightBalance route server=MyServer(ip=192.1.1.1, weight=3)
RandomWeightBalance route server=MyServer(ip=192.1.1.2, weight=5)
RandomWeightBalance route server=MyServer(ip=192.1.1.2, weight=5)
复制代码

3.3 DUBBO源码

public class RandomLoadBalance extends AbstractLoadBalance {
    public static final String NAME = "random";

    @Override
    protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {

        // invoker数量
        int length = invokers.size();

        // 所有权重是否相等
        boolean sameWeight = true;

        // 权重数组
        int[] weights = new int[length];

        // 第一个权重
        int firstWeight = getWeight(invokers.get(0), invocation);
        weights[0] = firstWeight;

        // 权重之和
        int totalWeight = firstWeight;

        // 遍历所有invoker
        for (int i = 1; i < length; i++) {

            // 获取权重
            int weight = getWeig
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值