Java实现负载均衡算法--轮询和加权轮询

java

1.普通轮询算法

轮询(Round Robin,RR)是依次将用户的访问请求,按循环顺序分配到web服务节点上,从1开始到最后一台服务器节点结束,然后再开始新一轮的循环。这种算法简单,但是没有考虑到每台节点服务器的具体性能,请求分发往往不均衡。

代码实现:

/**

* 普通轮询算法

*/public class RoundRobin {

private static Integer index = 0;

private static List<String> nodes = new ArrayList<>();

// 准备模拟数据

static {

nodes.add("192.168.1.101");

nodes.add("192.168.1.103");

nodes.add("192.168.1.102");

System.out.println("普通轮询算法的所有节点:"+nodes);//打印所有节点

}

// 关键代码

public String selectNode(){

String ip = null;

synchronized (index){

// 下标复位

if(index>=nodes.size()) index = 0;

ip = nodes.get(index);

index++;

}

return ip;

}

// 并发测试:两个线程循环获取节点

public static void main(String[] args) {

new Thread(() -> {

RoundRobin roundRobin1 = new RoundRobin();

for (int i=1;i<=5;i++){

String serverIp = roundRobin1.selectNode();

System.out.println(Thread.currentThread().getName()+"==第"+i+"次获取节点:"+serverIp);

}

}).start();

RoundRobin roundRobin2 = new RoundRobin();

for (int i=1;i<=nodes.size();i++){

String serverIp = roundRobin2.selectNode();

System.out.println(Thread.currentThread().getName()+"==第"+i+"次获取节点:"+serverIp);

}

}

}

执行结果:不同线程访问,结果依旧是按顺序循环分配节点

普通轮询算法的所有节点:[192.168.1.101, 192.168.1.103, 192.168.1.102]

main==第1次获取节点:192.168.1.101

Thread-0==第1次获取节点:192.168.1.103

Thread-0==第2次获取节点:192.168.1.102

Thread-0==第3次获取节点:192.168.1.101

Thread-0==第4次获取节点:192.168.1.103

Thread-0==第5次获取节点:192.168.1.102

main==第2次获取节点:192.168.1.101

main==第3次获取节点:192.168.1.103

2.加权轮询算法

加权轮询(Weighted Round Robin,WRR)是根据设定的权重值来分配访问请求,权重值越大的,被分到的请求数也就越多。一般根据每台节点服务器的具体性能来分配权重。

2.1.实现方式一

将需要轮询的所有节点按权重数循环生成一个List 集合,然后就跟普通轮询算法一样,来一个、分配一个、进1位。

例如:

所有节点信息:{{“192.168.1.100“,5},{“192.168.1.101“,1},{“192.168.1.102“,3}}

那么生成的List 集合为:

{“192.168.1.100“,

“192.168.1.100“,

“192.168.1.100“,

“192.168.1.100“,

“192.168.1.100“,

“192.168.1.101“,

“192.168.1.102“,

“192.168.1.102“,

“192.168.1.102“}

后面就是普通轮询算法的逻辑

代码实现:

类似于二维数组 降维成 一维数组,然后使用普通轮询

/**

* 简单版的加权轮询

*/public class WeightedRoundRobinSimple {

private static Integer index = 0;

private static Map<String,Integer> mapNodes = new HashMap<>();

// 准备模拟数据

static {

mapNodes.put("192.168.1.101",1);

mapNodes.put("192.168.1.102",3);

mapNodes.put("192.168.1.103",2);

/* -- 以下代码只为了方便查看所有节点,删除不影响 -- S */

List<String> nodes = new ArrayList<>();

Iterator<Map.Entry<String, Integer>> iterator = mapNodes.entrySet().iterator();

while (iterator.hasNext()){

Map.Entry<String, Integer> entry = iterator.next();

String key = entry.getKey();

for (int i=0;i<entry.getValue();i++){

nodes.add(key);

}

}

System.out.println("简单版的加权轮询:"+nodes);//打印所有节点

/* -- 以上代码只为了方便查看所有节点,删除不影响-- E */

}

// 关键代码:类似于二维数组 降维成 一维数组,然后使用普通轮询

public String selectNode(){

List<String> nodes = new ArrayList<>();

Iterator<Map.Entry<String, Integer>> iterator = mapNodes.entrySet().iterator();

while (iterator.hasNext()){

Map.Entry<String, Integer> entry = iterator.next();

String key = entry.getKey();

for (int i=0;i<entry.getValue();i++){

nodes.add(key);

}

}

String ip = null;

synchronized (index){

// 下标复位

if(index>=nodes.size()) index = 0;

ip = nodes.get(index);

index++;

}

return ip;

}

// 并发测试:两个线程循环获取节点

public static void main(String[] args) {

new Thread(() -> {

WeightedRoundRobinSimple roundRobin1 = new WeightedRoundRobinSimple();

for (int i=1;i<=6;i++){

String serverIp = roundRobin1.selectNode();

System.out.println(Thread.currentThread().getName()+"==第"+i+"次获取节点:"+serverIp);

}

}).start();

WeightedRoundRobinSimple roundRobin2 = new WeightedRoundRobinSimple();

for (int i=1;i<=6;i++){

String serverIp = roundRobin2.selectNode();

System.out.println(Thread.currentThread().getName()+"==第"+i+"次获取节点:"+serverIp);

}

}

}

执行结果:两个线程循环测试,输出结果会出现交替分配到不同的IP,但最终的效果都是一个个按顺序分配,类似于普通轮询算法。

简单版的加权轮询:[192.168.1.103, 192.168.1.103, 192.168.1.101, 192.168.1.102, 192.168.1.102, 192.168.1.102]

main==第1次获取节点:192.168.1.103

main==第2次获取节点:192.168.1.103

main==第3次获取节点:192.168.1.101

main==第4次获取节点:192.168.1.102

main==第5次获取节点:192.168.1.102

Thread-0==第1次获取节点:192.168.1.102

Thread-0==第2次获取节点:192.168.1.103

main==第6次获取节点:192.168.1.103

Thread-0==第3次获取节点:192.168.1.101

Thread-0==第4次获取节点:192.168.1.102

Thread-0==第5次获取节点:192.168.1.102

Thread-0==第6次获取节点:192.168.1.102

2.2.实现方式二(重点难点)

本文的重点难点。

在实现方式一的算法中可以很明显的看到,同权重的IP会被连续分配,也就是说同一个IP在短时间内收到不同的请求,过了这个连续点,就要等到下一轮才会被分配到,并没有做到均匀分配节点。

实现方式二将尽可能地均匀分配每个节点,节点分配不再是连续的,但最终的权重比和上一个方式一样,这种加权轮询又被称为平滑加权轮询。

理解关键的几个参数和算法逻辑,方便理解代码的实现。

2.2.1.概述

关键参数

回到顶部