java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java负载均衡算法

关于Java中常见的负载均衡算法

作者:Dreamlike223

这篇文章主要介绍了关于Java中常见的负载均衡算法,负载平衡是一种电子计算机技术,用来在多个计算机、网络连接、CPU、磁盘驱动器或其他资源中分配负载,以达到优化资源使用、最大化吞吐率、最小化响应时间、同时避免过载的目的,需要的朋友可以参考下

负载均衡

在这里插入图片描述

负载平衡(Load balancing)是一种电子计算机技术,用来在多个计算机(计算机集群)、网络连接、CPU、磁盘驱动器或其他资源中分配负载,以达到优化资源使用、最大化吞吐率、最小化响应时间、同时避免过载的目的。

使用带有负载平衡的多个服务器组件,取代单一的组件,可以通过冗余提高可靠性。

负载平衡服务通常是由专用软件和硬件来完成。

主要作用是将大量作业合理地分摊到多个操作单元上进行执行,用于解决互联网架构中的高并发和高可用的问题。

常见的负载均衡算法

在这里插入图片描述

1.轮询(Round Robin)

轮询算法按照顺序将新的请求分配给下一个服务器,最终实现平分请求。

实例:已知服务器: s1 ,s2, s3

请求1 -> s1

请求2-> s2

请求3 -> s3

请求4 -> s1

请求5 -> s2

请求6 -> s3

优点:

缺点:

java实现轮询算法:

思路:根据上面的介绍,依次的选择下一个服务器,轮询算法具有周期性的特性,这就是典型的周期性概念,我们第一想法应该就是取余了。

这里推荐大家《程序员的数学1》里面介绍了一些数学和编程思维的一些案例,其中就有介绍周期和分组的思想,个人感觉这本书还是不错的,推荐给大家。

public class RoundRobin {
    @Data
    public static class Server {
        private int serverId;
        private String name;
        private int weight;
        public Server(int serverId, String name) {
            this.serverId = serverId;
            this.name = name;
        }
        public Server(int serverId, String name, int weight) {
            this.serverId = serverId;
            this.name = name;
            this.weight = weight;
        }
    }
    private static AtomicInteger NEXT_SERVER_COUNTER = new AtomicInteger(0);
    private static int select(int modulo) {
        for (; ; ) {
            int current = NEXT_SERVER_COUNTER.get();
            int next = (current + 1) % modulo;
            boolean compareAndSet = NEXT_SERVER_COUNTER.compareAndSet(current, next);
            if (compareAndSet) {
                return next;
            }
        }
    }
    public static Server selectServer(List<Server> serverList) {
        return serverList.get(select(serverList.size()));
    }
    public static void main(String[] args) {
        List<Server> serverList = new ArrayList<>();
        serverList.add(new Server(1, "服务器1"));
        serverList.add(new Server(2, "服务器2"));
        serverList.add(new Server(3, "服务器3"));
        for (int i = 0; i < 10; i++) {
            Server selectedServer = selectServer(serverList);
            System.out.format("第%d次请求,选择服务器%s\n", i + 1, selectedServer.toString());
        }
    }
}

在这里插入图片描述

2.加权轮询(WeightedRound-Robin)

由于不同的服务器配置不同,因此它们处理请求的能力也不同,给配置高的机器配置相对较高的权重,让其处理更多的请求,给配置较低的机器配置较低的权重减轻期负载压力。

加权轮询可以较好的解决这个问题。

思路:

根据权重的大小让其获得相应被轮询到的机会。

已知:

服务器权重
s11
s22
s33

可以根据权重我们在内存中创建一个这样的数组{s1,s2,s2,s3,s3,s3},然后再按照轮询的方式选择相应的服务器。

缺点:

Nginx实现了一种平滑的加权轮询算法,可以将请求平滑(均匀)的分配到各个节点上。

下面我们用Java实现一下这个算法。

实现思路

我们以当前节点权重作为被选中的概率

 public void incrCurrentWeight() {
      this.currentWeight += weight;
 }

为了避免权重大的被连续选中,所以再被选中的时候我们应该让其的当前权重变小。我们可以采用

//当前权重 = 当前权重 - 总权重

1-6 =-5

3-6 =-3

可得权重越大下次当前权重变成最大的可能性也越大

public void selected(int total) {
    this.currentWeight -= total;
}

我们选取当前当前权重最大的一个服务器

public class WeightRoundRobin {
    @Data
    public static class Server {
        private int serverId;
        private String name;
        private int weight;
        private int currentWeight;
        public Server(int serverId, String name) {
            this.serverId = serverId;
            this.name = name;
        }
        public Server(int serverId, String name, int weight) {
            this.serverId = serverId;
            this.name = name;
            this.weight = weight;
        }
        public void selected(int total) {
            this.currentWeight -= total;
        }
        public void incrCurrentWeight() {
            this.currentWeight += weight;
        }
    }
    public static Server selectServer(List<Server> serverList) {
        int total = 0;
        Server selectedServer = null;
        int maxWeight = 0;
        for (Server server : serverList) {
            total += server.getWeight();
            server.incrCurrentWeight();
          	//选取当前权重最大的一个服务器
            if (selectedServer == null || maxWeight < server.getCurrentWeight()) {
                selectedServer = server;
                maxWeight = server.getCurrentWeight();
            }
        }
        if (selectedServer == null){
            Random random = new Random();
            int next = random.nextInt(serverList.size());
            return serverList.get(next);
        }
        selectedServer.selected(total);
        return selectedServer;
    }
    public static void main(String[] args) {
        List<Server> serverList = new ArrayList<>();
        serverList.add(new Server(1, "服务器1", 1));
        serverList.add(new Server(2, "服务器2", 3));
        serverList.add(new Server(3, "服务器3", 10));
        for (int i = 0; i < 10; i++) {
            Server server = selectServer(serverList);
            System.out.format("第%d次请求,选择服务器%s\n", i + 1, server.toString());
        }
    }

在这里插入图片描述

3.随机(Random)

思路:利用随机数从所有服务器中随机选取一台,可以用服务器数组下标获取。

public class RandomLoadBalance {
    @Data
    public static class Server {
        private int serverId;
        private String name;
        private int weight;
        public Server(int serverId, String name) {
            this.serverId = serverId;
            this.name = name;
        }
    }
    public static Server selectServer(List<Server> serverList) {
        Random selector = new Random();
        int next = selector.nextInt(serverList.size());
        return serverList.get(next);
    }
    public static void main(String[] args) {
        List<Server> serverList = new ArrayList<>();
        serverList.add(new Server(1, "服务器1"));
        serverList.add(new Server(2, "服务器2"));
        serverList.add(new Server(3, "服务器3"));
        for (int i = 0; i < 10; i++) {
            Server selectedServer = selectServer(serverList);
            System.out.format("第%d次请求,选择服务器%s\n", i + 1, selectedServer.toString());
        }
    }
}

在这里插入图片描述

4.加权随机(Weight Random)

思路:

这里我们是利用区间的思想,通过一个小于在此区间范围内的一个随机数,选中对应的区间(服务器),区间越大被选中的概率就越大。

已知:

服务器权重
s11
s22
s33

那么: 

s1:[0,1]
s2:(1,3]
s3 (3,6]
public class WeightRandom {
    @Data
    public static class Server {
        private int serverId;
        private String name;
        private int weight;
        public Server(int serverId, String name) {
            this.serverId = serverId;
            this.name = name;
        }
        public Server(int serverId, String name, int weight) {
            this.serverId = serverId;
            this.name = name;
            this.weight = weight;
        }
    }
    private static Server selectServer(List<Server> serverList) {
        int sumWeight = 0;
        for (Server server : serverList) {
            sumWeight += server.getWeight();
        }
        Random serverSelector = new Random();
        int nextServerRange = serverSelector.nextInt(sumWeight);
        int sum = 0;
        Server selectedServer = null;
        for (Server server : serverList) {
            if (nextServerRange >= sum && nextServerRange < server.getWeight() + sum) {
                selectedServer = server;
            }
            sum += server.getWeight();
        }
        return selectedServer;
    }
    public static void main(String[] args) {
        List<Server> serverList = new ArrayList<>();
        serverList.add(new Server(1, "服务器1", 1));
        serverList.add(new Server(2, "服务器2", 5));
        serverList.add(new Server(3, "服务器3", 10));
        for (int i = 0; i < 10; i++) {
            Server selectedServer = selectServer(serverList);
            System.out.format("第%d次请求,选择服务器%s\n", i + 1, selectedServer.toString());
        }
    }
}

在这里插入图片描述

5.IPHash

思路:根据每个每个请求ip(也可以是某个标识)ip.hash() % server.size()

public class IpHash {
    @Data
    public static class Server {
        private int serverId;
        private String name;
        public Server(int serverId, String name) {
            this.serverId = serverId;
            this.name = name;
        }
    }
    public static Server selectServer(List<Server> serverList, String ip) {
        int ipHash = ip.hashCode();
        return serverList.get(ipHash % serverList.size());
    }
    public static void main(String[] args) {
        List<Server> serverList = new ArrayList<>();
        serverList.add(new Server(1, "服务器1"));
        serverList.add(new Server(2, "服务器2"));
        serverList.add(new Server(3, "服务器3"));
        List<String> ips = Arrays.asList("192.168.9.5", "192.168.9.2", "192.168.9.3");
        for (int i = 0; i < 10; i++) {
            for (String ip : ips) {
                Server selectedServer = selectServer(serverList, ip);
                System.out.format("请求ip:%s,选择服务器%s\n", ip, selectedServer.toString());
            }
        }
    }
}

在这里插入图片描述

可以看到结果:同一ip肯定会命中同一台机器。

到此这篇关于关于Java中常见的负载均衡算法的文章就介绍到这了,更多相关Java负载均衡算法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文