负载均衡算法

目录

随机负载

随机从服务列表中挑选一个服务

// 基于Java的随机负载均衡算法
public class RandomLoadBalancer {
    
    // 传入服务器列表,返回随机选择的服务器
    public Server selectServer(List<Server> serverList) {
        // 获取服务器列表大小
        int size = serverList.size();
        // 生成随机数
        int randomIndex = new Random().nextInt(size);
        // 返回随机选择的服务器
        return serverList.get(randomIndex);
    }
    
    // 服务器类
    public static class Server {
        private String ip;
        private int port;
        
        public Server(String ip, int port) {
            this.ip = ip;
            this.port = port;
        }
        
        // getter和setter方法省略
    }
}

轮询负载

遍历服务列表(排好队一个一个来)

public class RoundRobinLoadBalancer {
    
    // 记录上一次选择的服务器下标
    private int lastIndex = -1;
    
    // 传入服务器列表,返回轮询选择的服务器
    public Server selectServer(List<Server> serverList) {
        // 获取服务器列表大小
        int size = serverList.size();
        // 如果服务器列表为空,返回null
        if (size == 0) {
            return null;
        }
        // 如果只有一个服务器,直接返回该服务器
        if (size == 1) {
            return serverList.get(0);
        }
        // 上锁,保证线程安全
        synchronized (this) {
            // 如果上一次选择的服务器下标超出了服务器列表大小,重置为-1
            if (lastIndex >= size) {
                lastIndex = -1;
            }
            // 选择下一个服务器
            int index = lastIndex + 1;
            lastIndex = index;
            // 返回选择的服务器
            return serverList.get(index);
        }
    }
    
    // 服务器类
    public static class Server {
        private String ip;
        private int port;
        
        public Server(String ip, int port) {
            this.ip = ip;
            this.port = port;
        }
        
        // getter和setter方法省略
    }
}

加权负载

等于是给每个服务加了一个访问比例,又可以细分为随机加权负载、轮询加权负载,区别如下:

假设有3台服务、权重为3/5/2:
server1    3
server2    5
server3    2

随机加权负载:就是按照几率选择服务,,这里提供一个方案:

初始化一个容量为10的数组,随机装载上以上服务id,比如server2就会占有5个下标,获取服务的时候获取一个随机数0-9,取对应下标的服务,如果服务已经被取过了则下标+1,往上推,到了边界则置0再往上推,如果全被取过一次了,则重新装载

轮询加权负载:就是挨个遍历结果,这里提供一个比较好的示例:

public class WeightedRoundRobinLoadBalancer {
    // 记录上一次选择的服务器下标
    private int lastIndex = -1;
    // 记录当前权重
    private int currentWeight = 0;
    // 最大权重
    private int maxWeight;
    // 权重的最大公约数
    private int gcdWeight;
    // 服务器列表
    private List<Server> serverList;

    public WeightedRoundRobinLoadBalancer(List<Server> serverList) {
        this.serverList = serverList;
        init();
    }

    // 初始化
    private void init() {
        // 获取最大权重
        maxWeight = getMaxWeight();
        // 获取权重的最大公约数
        gcdWeight = getGcdWeight();
    }

    // 传入服务器列表,返回加权轮询选择的服务器
    public Server selectServer() {
        while (true) {
            // 上一次选择的服务器下标加1
            lastIndex = (lastIndex + 1) % serverList.size();
            // 如果上一次选择的服务器下标为0,重新计算当前权重
            if (lastIndex == 0) {
                currentWeight = currentWeight - gcdWeight;
                if (currentWeight <= 0) {
                    currentWeight = maxWeight;
                    if (currentWeight == 0) {
                        return null;
                    }
                }
            }
            // 获取当前下标的服务器
            Server server = serverList.get(lastIndex);
            // 如果当前服务器的权重大于等于当前权重,返回该服务器
            if (server.getWeight() >= currentWeight) {
                return server;
            }
        }
    }

    // 获取最大权重
    private int getMaxWeight() {
        int maxWeight = 0;
        for (Server server : serverList) {
            int weight = server.getWeight();
            if (weight > maxWeight) {
                maxWeight = weight;
            }
        }
        return maxWeight;
    }

    // 获取权重的最大公约数
    private int getGcdWeight() {
        int gcdWeight = 0;
        for (Server server : serverList) {
            int weight = server.getWeight();
            if (gcdWeight == 0) {
                gcdWeight = weight;
            } else {
                gcdWeight = gcd(gcdWeight, weight);
            }
        }
        return gcdWeight;
    }

    // 求最大公约数
    private int gcd(int a, int b) {
        if (b == 0) {
            return a;
        } else {
            return gcd(b, a % b);
        }
    }

    // 服务器类
    public static class Server {
        private String ip;
        private int port;
        private int weight; // 权重

        public Server(String ip, int port, int weight) {
            this.ip = ip;
            this.port = port;
            this.weight = weight;
        }
        // getter和setter方法省略
    }


    public static void main(String[] args) {
        List<Server> servers=new ArrayList<>();
        servers.add(new Server("1", 1, 3));
        servers.add(new Server("2", 2, 5));
        servers.add(new Server("3", 3, 2));

        WeightedRoundRobinLoadBalancer weightedRoundRobinLoadBalancer = new WeightedRoundRobinLoadBalancer(servers);
        System.out.println(JSON.toJSON(weightedRoundRobinLoadBalancer.selectServer()));
        System.out.println(JSON.toJSON(weightedRoundRobinLoadBalancer.selectServer()));
        System.out.println(JSON.toJSON(weightedRoundRobinLoadBalancer.selectServer()));
        System.out.println(JSON.toJSON(weightedRoundRobinLoadBalancer.selectServer()));
        System.out.println(JSON.toJSON(weightedRoundRobinLoadBalancer.selectServer()));
        System.out.println(JSON.toJSON(weightedRoundRobinLoadBalancer.selectServer()));
        System.out.println(JSON.toJSON(weightedRoundRobinLoadBalancer.selectServer()));
        System.out.println(JSON.toJSON(weightedRoundRobinLoadBalancer.selectServer()));
        System.out.println(JSON.toJSON(weightedRoundRobinLoadBalancer.selectServer()));
        System.out.println(JSON.toJSON(weightedRoundRobinLoadBalancer.selectServer()));
        System.out.println(JSON.toJSON(weightedRoundRobinLoadBalancer.selectServer()));

    }
}

IP_HASH负载

就是根据IP的Hash值取余

public class HashLoadBalancer {
    
    // 服务器列表
    private List<Server> serverList;
    
    public HashLoadBalancer(List<Server> serverList) {
        this.serverList = serverList;
    }
    
    // 传入客户端IP,返回选择的服务器
    public Server selectServer(String clientIp) {
        // 将客户端IP进行Hash
        int hashCode = clientIp.hashCode();
        // 取模得到服务器下标
        int index = hashCode % serverList.size();
        // 返回对应下标的服务器
        return serverList.get(index);
    }
    
    // 服务器类
    public static class Server {
        private String ip;
        private int port;
        
        public Server(String ip, int port) {
            this.ip = ip;
            this.port = port;
        }
        // getter和setter方法省略
    }
}

还有响应时间负载、异常率负载等这种动态指标的就不演示了,因为需要接入指标,大体负载逻辑都大同小异

Last Updated: