负载均衡算法

本文涉及的产品
应用型负载均衡 ALB,每月750个小时 15LCU
网络型负载均衡 NLB,每月750个小时 15LCU
简介: 负载均衡算法 负载均衡算法说明 负载均衡介绍 负载均衡方式 软件负载均衡 硬件负载均衡 负载均衡算法 负载均衡算法模拟 数据支持 (1) 随机算法 1、简单随机算法 2、加权随机算法——V1 3、加权

负载均衡算法

负载均衡算法说明
  • 负载均衡介绍
  • 负载均衡,英文名称为Load Balance,指由多台服务器以对称的方式组成一个服务器集合,每台服务器都具有等价的地位,都可以单独对外提供服务而无须其他服务器的辅助。
  • 通过某种负载分担技术,将外部发送过来的请求均匀分配到对称结构中的某一台服务器上,而接收到请求的服务器独立地回应客户的请求。
  • 负载均衡能够平均分配客户请求到服务器阵列,借此提供快速获取重要数据,解决大量并发访问服务问题,这种集群技术可以用最小的投资获得接近于大型主机的性能。
  • 负载均衡方式
软件负载硬件负载
  • 软件负载均衡
  • 常见的负载均衡软件有:nginx,LVS,HAproxy
  • 硬件负载均衡
  • 常见的负载均硬件件有:Array,F5
  • 负载均衡算法
随机算法,加权轮询,一致性hash,最小活跃数算法
负载均衡算法模拟
  • 数据支持
  • 在这里插入图片描述
(1) 随机算法
1、简单随机算法
  • 在这里插入图片描述
这个简单随机算法使用与每天机器的性能差不多的时候,实际上,生产中可能某些机器的性能更高一点,它可以处理更多的情况,所以,我们可以对每台服务器设置一个权重。
2、加权随机算法——V1
  • 在这里插入图片描述
这个V1版本的加权随机算法的思路比较简单,每个服务器按它所对应的的权重进行复制。
这种实现方法在遇到权重之和特别大的时候就会比较消耗内存,因为需要对ip地址进行复制,权重之和越大那么上文中的ips就需要越多的内存。
3、加权随机算法——V2
下面,介绍另一种实现思路。
假设有一组服务器servers=[A,B,C],对应的权重weights=[5,3,2],权重总和为10。
(1)现在把这些权重平铺在一维坐标上,那么就会有[0,5]区间属于服务器A,[5,8]区间属于服务器B,[8,10]区间属于服务器C。
(2)接下来通过随机数生成一个范围在[0,10]之间的随机数,然后计算该随机数落在哪个区间上即可。
  • 在这里插入图片描述
(2) 轮询算法
1、简单轮询算法
  • 在这里插入图片描述
这种简单轮询算法,很公平,每台服务器轮流来进行服务,但是有的机器性能好,所以 能者多劳,和随机算法一样,所以,我们可以对每台服务器设置一个权重。
2、加权轮询算法
  • 在这里插入图片描述

加权轮询算法:
思想:

  1. 例如有服务器servers=[A,B,C],对应权重weights=[2,5,1],总权重为8。
  2. 我们可以理解为有8台服务器,2台A,5台B,1台C,一次调用过来的时候,需
  3. 要按顺序访问,如有10次调用,调用顺序为AABBBBBCAA。

步骤:

  1. 因为调用次数会越来越大,而服务器是固定,需要将调用次数“缩小”,取余
  2. 将权重值平铺在一维坐标值上:[0,2]为A,[2,7]为B,[7,8]为C
  3. 接下来获取该次是第几次请求,需要对总权重做取余运算,获取offset

这种算法有一个缺点:一台服务器的权重特别大的时候,他需要连续的处理请求,但是实际上我们想达到的效果是,对于100次请求,只要有有100*8/50=16次就够了,这16次不一定要连续的访问,比如假设我们有三台服务器servers=[A,B,C],对应权重weights=[2,5,1],总权重为7,那么上述这算法的结果是AAAAABC,那么如果能够是这么一个结果呢:AABACAA,把B和C平均插入到5个A中间,这样是比较均衡的。

3、平滑加权轮询算法

那么就引出了平滑加权轮询
思路:

  1. 每个服务器对应两个权重,分别为weight和currentWeight。其中weight是固定的,currentWeight会动态调整,初始值为0
  2. 当有新的请求进来时,遍历服务器列表,让它的currentWeight加上自身权重,遍历完成后,找到最大的currentWeight。
  3. 并将最大的currentWeight减去权重总和,然后返回相应的服务器即可。

假设:
测试数据:

WEIGHT_LIST.put("A", 5);
WEIGHT_LIST.put("B", 1);
WEIGHT_LIST.put("C", 1);

运算过程如下:

| 次数| 当前currentWeight数组 (currentWeight+=weight)| 选择结果max(currentWeight) |减去权重总和后的currentWeight数组 (max(currentWeight)-=sum(weight)) |

1 [5,1,1] A [-2,1,1]
2 [3,2,2] A [-4,2,2]
3 [1,3,3] B [1,-4,3]
4 [6,-3,4] A [-1,-3,4]
5 [4,-2,5] C [4,-2,-2]
6 [9,-1,-1] A [2,-1,-1]
7 [7,0,0] A [0,0,0]
8 [5,1,1] A [-2,1,1]
如表所示,经过平滑行处理后,得到的服务器序列为[A,A,B,A,C,A,A],相比之前的序列[A,A,A,A,A,B,C],分布性要好一些。初始情况下currentWeight=[0,0,0] ,在第7个请求处理完后,currentWeight再次变回[0,0,0]。
你会惊讶的发现在第8次的时候,当前currentWeight数组又变回了[5,1,1] !!!
  • 具体代码实现如下图:
  • 在这里插入图片描述
(3) 一致性哈希算法
服务器集群接收到一次请求调用时,可以根据请求的信息,比如客户端的ip地址,或请求路径与参数等信息进行哈希,可以得出一个哈希值,特点是对于相同的ip地址,或请求路径和请求参数哈希出来的值是一样,只要能再增加一个算法,能够把这个哈希值映射成一个服务端ip的地址,就可以使相同的请求落到同一服务器上。

因为客户端发起的请求情况是无穷大的,所以对于哈希值也是无穷大的,所以不能把所有的哈希值都进行映射到服务器ip上,所以这里需要用到哈希环。如下图:

  • 在这里插入图片描述
上面的情况是比较均匀,如果出现ip4服务器宕机了,那就是这样的了:
  • 在这里插入图片描述
会发现ip3和ip1直接的范围是比较大的,会有更多的请求落到ip1上,这是不公平的,解决这个问题需要加入虚拟节点:
  • 在这里插入图片描述
其中ip2-1,ip3-1就是虚拟结点,并不能处理节点,而是等同于对应的ip2和ip3服务器。
实际上,这只是处理这种不均衡性的一种思路,实际上就算哈希环本身是均衡的,你也可以增加更多的虚拟节点来使这个环更加平衡,比如:
  • 在这里插入图片描述

这个彩环也是公平的,并且只有ip1,ip2,ip3,ip4是实际的服务器ip,其他的都是虚拟ip。
那么我们怎么实现呢?

  1. 对于我们的服务器ip地址,我们肯定是知道共有多少个,需要多少个虚拟节点也是我们自己控制,虚拟节点多则流量越均衡,另外哈希算法也是很关键的,哈希算法越散列流量也将越均衡。
  2. 这种环,可以使用TreeMap来存储;当一次请求过来,获取该请求的hash值,根据hash值从TreeMap中,拿大于该hash值的子树。
  3. 再从得到的子树中,拿第一个元素即可。
  • 具体代码实现:
  • 在这里插入图片描述
(4) 最小活跃数算法
前面几种方法主要目标是使服务端分配到的调用次数尽量均衡,但是实际情况是这样吗?
调用次数相同,服务器的负载就均衡吗?
当然不是,这里还要考虑每次调用的时间,而最小活跃数算法则是解决这种问题的。

活跃调用数越小,表明该服务提供者效率越高,单位时间内可处理更多的请求。此时应优先将请求分配给该服
务提供者。在具体实现中,每个服务提供者对应一个活跃数。初始情况下,所有服务提供者活跃数均为0。每
收到一个请求,活跃数加1,完成请求后则将活跃数减1。在服务运行一段时间后,性能好的服务提供者处理请
求的速度更快,因此活跃数下降的也越快,此时这样的服务提供者能够优先获取到新的服务请求、这就是最小
活跃数负载均衡算法的基本思想。除了最小活跃数,最小活跃数算法在实现上还引入了权重值。所以准确的来
说,最小活跃数算法是基于加权最小活跃数算法实现的。举个例子说明一下,在-一个服务提供者集群中,有两
个性能优异的服务提供者。某一时刻它们的活跃数相同,则会根据它们的权重去分配请求,权重越大,获取到
新请求的概率就越大。如果两个服务提供者权重相同,此时随机选择一个即可。

实现:
因为活跃数是需要服务器请求处理相关逻辑配合的,- -次调用开始时活跃数+1,结束是活跃数-1, 所以这里就
不对这部分逻辑进行模拟了,直接使用一个map来进行模拟。

  • 具体代码实现:
//最小活跃算法
public class LeastActive {
    private static String getServer() {
        //找出当前活跃数最小的服务器
        Optional<Integer> minValue = ServerIps.ACTIVITY_LIST.values().stream().min(Comparator.naturalOrder());
        if (minValue.isPresent()) {
            List<String> minActivityIps = new ArrayList<>();
            ServerIps.ACTIVITY_LIST.forEach((ip, activity) -> {
                if (activity.equals(minValue.get())) {
                    minActivityIps.add(ip);
                }
            });
            //最小活跃数的ip有多个,则根据权重来选,权重大的优先
            if (minActivityIps.size() > 1) {
                //过滤出对应的ip和权重
                Map<String, Integer> weightList = new LinkedHashMap<>();
                ServerIps.WEIGHT_LIST.forEach((ip, weight) -> {
                    if (minActivityIps.contains(ip)) {
                        weightList.put(ip, ServerIps.WEIGHT_LIST.get(ip));
                    }
                });
                int totalWeight = 0;
                boolean sameWeight = true;
                Object[] weights = weightList.values().toArray();
                //计算出总的权重,判断所有权重是否一样
                for (int i = 0; i < weights.length; i++) {
                    Integer weight = (Integer) weights[i];
                    totalWeight += weight;
                    if (sameWeight && i > 0 && !weight.equals(weights[i - 1])) {
                        sameWeight = false;
                    }
                }
                //生成一个在[0,totalWeight]区间内的随机数
                java.util.Random random = new java.util.Random();
                int randomPos = random.nextInt(totalWeight);
                if (!sameWeight) {
                    for (String ip : weightList.keySet()) {
                        Integer weight = weightList.get(ip);
                        if (randomPos < weight) {
                            return ip;
                        }
                        randomPos = randomPos - weight;
                    }
                }
                //如果所有权重都一样,就使用随机算法
                randomPos = random.nextInt(weightList.size());
                return (String) weightList.keySet().toArray()[randomPos];
            } else {
                return minActivityIps.get(0);
            }
        } else {
            java.util.Random random = new java.util.Random();
            int randomPos = random.nextInt(ServerIps.WEIGHT_LIST.size());
            return (String) ServerIps.WEIGHT_LIST.keySet().toArray()[randomPos];
        }
    }
    public static void main(String[] args) {
        for (int i=0; i<10; i++){
            System.out.println(getServer());
        }
    }
}
相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
目录
相关文章
|
3月前
|
负载均衡 算法 Java
Spring Cloud全解析:负载均衡算法
本文介绍了负载均衡的两种方式:集中式负载均衡和进程内负载均衡,以及常见的负载均衡算法,包括轮询、随机、源地址哈希、加权轮询、加权随机和最小连接数等方法,帮助读者更好地理解和应用负载均衡技术。
|
21天前
|
存储 负载均衡 算法
负载均衡算法
负载均衡算法
23 1
|
2月前
|
负载均衡 算法 搜索推荐
Nginx 常用的负载均衡算法
【10月更文挑战第17天】在实际应用中,我们需要根据具体的情况来选择合适的负载均衡算法。同时,还可以结合其他的优化措施,如服务器健康检查、动态调整权重等,来进一步提高负载均衡的效果和系统的稳定性。
117 59
|
4月前
|
负载均衡 NoSQL 算法
一天五道Java面试题----第十天(简述Redis事务实现--------->负载均衡算法、类型)
这篇文章是关于Java面试中Redis相关问题的笔记,包括Redis事务实现、集群方案、主从复制原理、CAP和BASE理论以及负载均衡算法和类型。
一天五道Java面试题----第十天(简述Redis事务实现--------->负载均衡算法、类型)
|
27天前
|
负载均衡 算法 应用服务中间件
5大负载均衡算法及原理,图解易懂!
本文详细介绍负载均衡的5大核心算法:轮询、加权轮询、随机、最少连接和源地址散列,帮助你深入理解分布式架构中的关键技术。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
5大负载均衡算法及原理,图解易懂!
|
26天前
|
负载均衡 算法
SLB-Backend的负载均衡算法
【10月更文挑战第19天】
44 5
|
1月前
|
负载均衡 算法 应用服务中间件
Nginx 常用的负载均衡算法
【10月更文挑战第22天】不同的负载均衡算法各有特点和适用场景。在实际应用中,需要根据具体的业务需求、服务器性能和网络环境等因素来选择合适的算法。
34 3
|
2月前
|
缓存 负载均衡 算法
nginx学习:配置文件详解,负载均衡三种算法学习,上接nginx实操篇
Nginx 是一款高性能的 HTTP 和反向代理服务器,也是一个通用的 TCP/UDP 代理服务器,以及一个邮件代理服务器和通用的 HTTP 缓存服务器。
76 0
nginx学习:配置文件详解,负载均衡三种算法学习,上接nginx实操篇
|
2月前
|
负载均衡 监控 算法
每个程序员都应该知道的 6 种负载均衡算法
每个程序员都应该知道的 6 种负载均衡算法
119 2
|
4月前
|
负载均衡 监控 算法
揭秘负载均衡的五大算法秘籍:让你的服务器轻松应对亿万流量,不再崩溃!
【8月更文挑战第31天】在互联网快速发展的今天,高可用性和可扩展性成为企业关注的重点。负载均衡作为关键技术,通过高效分配网络流量提升系统处理能力。本文介绍了轮询、加权轮询、最少连接及IP哈希等常见负载均衡算法及其应用场景,并提供Nginx配置示例。此外,还探讨了如何根据业务需求选择合适算法、配置服务器权重、实现高可用方案、监控性能及定期维护等最佳实践,助力系统优化与用户体验提升。
74 2