所谓负载均衡就是将外部发送过来的请求均匀或者根据某种算法分配到对称结构中的某一台服务器中。负载均衡可以分为硬件负载均衡和软件负载均衡,常见的硬件负载均衡有F5、Array等,但是这些设备都比较昂贵。相比之下,利用软件来实现负载均衡就比较简单了,常见的像是 Nginx 的反向代理负载均衡。
这篇文章并不去细说 Nginx 这类软件的具体配置,只是着重来了解几种常见的负载均衡算法的实现(本文使用Java描述)与应用。对于我们来讲,所了解的最基本的负载均衡算法包括了:随机、轮询、一致性Hash等几种方式,接下来就来详细介绍这几种算法:
一、随机#
1. 完全随机
所谓完全随机就是完全没有规律可言,每次随机种IP列表中选取一个,将请求打到该服务器上:
public class ServerIps {
public static final List<String> LIST = Arrays.asList(
"192.168.0.1",
"192.168.0.2",
"192.168.0.3",
"192.168.0.4",
"192.168.0.5"
);
}
public class Random {
public static String getServer() {
java.util.Random = new java.util.Random();
int rand = random.nextInt(ServerIps.LIST.size());
return ServerIps.LIST.get(rand);
}
public static void main(String[] args) {
for(int i = 0; i < 10; i++) {
System.out.println(getServer());
}
}
}
完全随机是最简单的负载均衡算法了,实现起来也很简单。但是我们在日常生产过程中,机器的处理效率肯定是不同的,有些机器处理得快,有些机器处理得慢,完全随机的缺点在此刻就很明显了,无法将更多的请求分配到更好的服务器上,由此就有了加权随机的思想了。
2. 加权随机#
加权随机常见的有两种实现方式,现在先来了解第一种方式,先构建一个ServerIps类先,如下:
public class ServerIps {
public static final Map<String, Integer> WEIGHT_LIST = new LinkedHashMap<String, Integer>();
static {
WEIGHT_LIST.put("192.168.0.1", 1);
WEIGHT_LIST.put("192.168.0.2", 8);
WEIGHT_LIST.put("192.168.0.3", 3);
WEIGHT_LIST.put("192.168.0.4", 6);
WEIGHT_LIST.put("192.168.0.5", 5);
}
}
首先这种实现方式的想法很简单,就是通过上面的服务器IP的List,再去构建一次List:如果一个IP服务器的权重为8,那么就往List里面添加8次该对应的IP地址,如果权重为3,那么就添加3次,以此类推。这样的话,结合前面的完全随机实现,那么这样权重越大的,在List中出现的比例也越高,被选中的几率当然也会更大:
public class WeightRandom {
public static String getServer() {
// 构建一个新的List
List<String> ips = new ArrayList<>();
for(String ip : ServerIps.WEIGHT_LIST.keySet()) {
Integer weight = ServerIps.WEIGHT_LIST.get(ip);
for(int i = 0; i < weight; i++) {
ips.add(ip);
}
}
java.util.Random random = new java.util.Random();
int randomPos = random.nextInt(ips.size());
return ips.get(randomPos);
}
public static void main(String[] args) {
// 连续调用10次
for(int i = 0; i < 10; i++) {
System.out.println(getServer());
}
}
}
上面代码可以自行测试一下,最后结构应该就是权重高的服务器被选中的几率更高。但是这也有一种很明显的缺点,如果现在的权重是像下面这样配置呢?
WEIGHT_LIST.put("192.168.0.1", 998);
WEIGHT_LIST.put("192.168.0.2", 13);
// ...
这样很明显就会带来一个新的问题,服务器中的新建的List动不动就会有上万条记录,那么此时服务器岂不是要白白浪费掉一部分内存去维护一个数组,所以此时我们就有了下面更优的实现。
3. 加权随机优化
假设我们现在有3台服务器,A服务器的权重为3,B服务器的权重为5,C服务器的权重为1。然后我们随机生成一个数:
-
如果生成的随机数为1,1 ≤ 3,那么此时请求在落在A的区间段,请求应该交由A来处理;
-
如果生成的随机数为5,那么此时 3 < 5,所以不在A区间,而 5 - 3 = 2 < 5,那么此时应该落在B区间;
-
如果生成的随机数为9,那么 9 > 3 + 5,所以不可