共计 5117 个字符,预计需要花费 13 分钟才能阅读完成。
这篇文章主要介绍“服务器的负载均衡算法怎么实现”,在日常操作中,相信很多人在服务器的负载均衡算法怎么实现问题上存在疑惑,丸趣 TV 小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”服务器的负载均衡算法怎么实现”的疑惑有所帮助!接下来,请跟着丸趣 TV 小编一起来学习吧!
负载平衡(Load balancing)是一种在多个计算机(网络、CPU、磁盘)之间均匀分配资源,以提高资源利用的技术。使用负载均衡可以最大化服务吞吐量,可能最小化响应时间,同时由于使用负载均衡时,会使用多个服务器节点代单点服务,也提高了服务的可用性。
负载均衡的实现可以软件可以硬件,硬件如大名鼎鼎的 F5 负载均衡设备,软件如 NGINX 中的负载均衡实现,又如 Springcloud Ribbon 组件中的负载均衡实现。
如果看到这里你还不知道负载均衡是干嘛的,那么只能放一张图了,毕竟没图说个啥。
负载均衡要做到在多次请求下,每台服务器被请求的次数大致相同。但是实际生产中,可能每台机器的性能不同,我们会希望性能好的机器承担的请求更多一些,这也是正常需求。
如果这样说下来你看不懂,那我就再举个例子好了,一排可爱的小熊(服务器)站好。
这时有人(用户)要过来打脸(请求访问)。
那么怎么样我们才能让这每一个可爱的小熊被打的次数大致相同呢?
又或者熊 4 比较胖,抗击打能力是别人的两倍,我们怎么提高熊 4 被打的次数也是别人的两倍呢?
又或者每次出手的力度不同,有重有轻,恰巧熊 4 总是承受这种大力度啪啪打脸,熊 4 即将不省熊事,还要继续打它吗?
这些都是值的思考的问题。
说了那么多,口干舌燥,我双手已经饥渴难耐了,迫不及待的想要撸起代码了。
1. 随机访问
上面说了,为了负载均衡,我们必须保证多次出手后,熊 1 到熊 4 被打次数均衡。比如使用随机访问法,根据数学上的概率论,随机出手次数越多,每只熊被打的次数就会越相近。代码实现也比较简单,使用一个随机数,随机访问一个就可以了。
/** 服务器列表 */
private static List String serverList =
new ArrayList ();
static {
serverList.add(192.168.1.2
serverList.add(192.168.1.3
serverList.add(192.168.1.4
serverList.add(192.168.1.5
}
/**
* 随机路由算法
*/
public static String
random() {
// 复制遍历用的集合,防止操作中集合有变更
List String tempList = new ArrayList (serverList.size());
tempList.addAll(serverList);
// 随机数随机访问
int randomInt = new Random().nextInt(tempList.size());
return tempList.get(randomInt);
}
因为使用了非线程安全的集合,所以在访问操作时操作的是集合的拷贝,下面几种轮训方式中也是这种思想。
写一个模拟请求方法,请求 10w 次,记录请求结果。
public static void main(String[] args) {
HashMap String, Integer serverMap =
new HashMap ();
for (int i =
0; i
20000; i++) {
String server = random();
Integer count = serverMap.get(server);
if (count ==
null) {
count =
1;
}
else {
count++;
}
// 记录
serverMap.put(server, count);
}
// 路由总体结果
for (Map.Entry String, Integer entry : serverMap.entrySet()) {
System.out.println(IP: + entry.getKey() +,次数:+ entry.getValue());
}
}
运行得到请求结果。
IP:192.168.1.3,次数:24979
IP:192.168.1.2,次数:24896
IP:192.168.1.5,次数:25043
IP:192.168.1.4,次数:25082
每台服务器被访问的次数都趋近于 2.5w,有点负载均衡的意思。但是随机毕竟是随机,是不能保证访问次数绝对均匀的。
2. 轮训访问
轮训访问就简单多了,拿上面的熊 1 到熊 4 来说,我们一个接一个的啪啪 – 打脸,熊 1 打完打熊 2,熊 2 打完打熊 3,熊 4 打完打熊 1,最终也是实现了被打均衡。但是保证均匀总是要付出代价的,随机访问中需要随机,轮训访问中需要什么来保证轮训呢?
/** 服务器列表 */
private static List String serverList =
new ArrayList ();
static {
serverList.add(192.168.1.2
serverList.add(192.168.1.3
serverList.add(192.168.1.4
serverList.add(192.168.1.5
}
private static Integer index =
0;
/**
* 随机路由算法
*/
public static String
randomOneByOne() {
// 复制遍历用的集合,防止操作中集合有变更
List String tempList = new ArrayList (serverList.size());
tempList.addAll(serverList);
String server =
synchronized (index) {
index++;
if (index == tempList.size()) {
index = 0;
}
server = tempList.get(index);;
}
return server;
}
由代码里可以看出来,为了保证轮训,必须记录上次访问的位置,为了让在并发情况下不出现问题,还必须在使用位置记录时进行加锁,很明显这种互斥锁增加了性能开销。
依旧使用上面的测试代码测试 10w 次请求负载情况。
IP:192.168.1.3,次数:25000
IP:192.168.1.2,次数:25000
IP:192.168.1.5,次数:25000
IP:192.168.1.4,次数:25000
3. 轮训加权
上面演示了轮训方式,还记的一开始提出的熊 4 比较胖抗击打能力强,可以承受别人 2 倍的挨打次数嘛?上面两种方式都没有体现出来熊 4 的这个特点,熊 4 窃喜,不痛不痒。但是熊 1 到 熊 3 已经在崩溃的边缘,不行,我们必须要让胖着多打,能者多劳,提高整体性能。
/** 服务器列表 */
private static HashMap String, Integer serverMap =
new HashMap ();
static {
serverMap.put( 192.168.1.2 ,
2);
serverMap.put( 192.168.1.3 ,
2);
serverMap.put( 192.168.1.4 ,
2);
serverMap.put( 192.168.1.5 ,
4);
}
private static Integer index =
0;
/**
* 加权路由算法
*/
public static String
oneByOneWithWeight() {
List String tempList =
new ArrayList();
HashMap String, Integer tempMap =
new HashMap ();
tempMap.putAll(serverMap);
for (String key : serverMap.keySet()) {
for (int i =
0; i serverMap.get(key); i++) {
tempList.add(key);
}
}
synchronized (index) {
index++;
if (index == tempList.size()) {
index =
0;
}
return tempList.get(index);
}
}
这次记录下了每台服务器的整体性能,给出一个数值,数值越大,性能越好。可以承受的请求也就越多,可以看到服务器 192.168.1.5 的性能为 4,是其他服务器的两倍,依旧 10 w 请求测试。
IP:192.168.1.3,次数:20000
IP:192.168.1.2,次数:20000
IP:192.168.1.5,次数:40000
IP:192.168.1.4,次数:20000
192.168.1.5 承担了 2 倍的请求。
4. 随机加权
随机加权的方式和轮训加权的方式大致相同,只是把使用互斥锁轮训的方式换成了随机访问,按照概率论来说,访问量增多时,服务访问也会达到负载均衡。
/** 服务器列表 */
private static HashMap String, Integer serverMap =
new HashMap ();
static {
serverMap.put( 192.168.1.2 ,
2);
serverMap.put( 192.168.1.3 ,
2);
serverMap.put( 192.168.1.4 ,
2);
serverMap.put( 192.168.1.5 ,
4);
}
/**
* 加权路由算法
*/
public static String
randomWithWeight() {
List String tempList =
new ArrayList();
HashMap String, Integer tempMap =
new HashMap ();
tempMap.putAll(serverMap);
for (String key : serverMap.keySet()) {
for (int i =
0; i serverMap.get(key); i++) {
tempList.add(key);
}
}
int randomInt =
new Random().nextInt(tempList.size());
return tempList.get(randomInt);
}
依旧 10 w 请求测试,192.168.1.5 的权重是其他服务器的近似两倍,
IP:192.168.1.3,次数:19934
IP:192.168.1.2,次数:20033
IP:192.168.1.5,次数:39900
IP:192.168.1.4,次数:20133
5. IP-Hash
上面的几种方式要么使用随机数,要么使用轮训,最终都达到了请求的负载均衡。但是也有一个很明显的缺点,就是同一个用户的多次请求很有可能不是同一个服务进行处理的,这时问题来了,如果你的服务依赖于 session,那么因为服务不同,session 也会丢失,不是我们想要的,所以出现了一种根据请求端的 ip 进行哈希计算来决定请求到哪一台服务器的方式。这种方式可以保证同一个用户的请求落在同一个服务上。
private static List String serverList =
new ArrayList ();
static {
serverList.add(192.168.1.2
serverList.add(192.168.1.3
serverList.add(192.168.1.4
serverList.add(192.168.1.5
}
/**
* ip hash 路由算法
*/
public static String
ipHash(String ip) {
// 复制遍历用的集合,防止操作中集合有变更
List String tempList = new ArrayList (serverList.size());
tempList.addAll(serverList);
// 哈希计算请求的服务器
int index = ip.hashCode() % serverList.size();
return tempList.get(Math.abs(index));
}
到此,关于“服务器的负载均衡算法怎么实现”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注丸趣 TV 网站,丸趣 TV 小编会继续努力为大家带来更多实用的文章!