图解 6 种常见负载均衡算法!

向量数据库大模型微服务

picture.image

苏三的免费八股文网站: www.susan.net.cn

负载均衡是指将来自客户端的请求分配到多个服务器上进行处理,从而有效地提高系统性能、可用性和可扩展性。

常见的负载均衡算法包括轮询、加权轮询、随机、加权随机、源IP哈希和最少连接等。下面将逐一介绍它们。

轮询算法(Round Robin)

轮询算法是最简单和最常见的负载均衡算法之一,其实现思路也非常直接:按预定顺序将请求依次转发到后端服务器。通常要求服务实例是无状态的。

如下图所示:

picture.image

  • 第一个请求首先发送到第一个服务器A;
  • 第二个请求发送到下一个服务器,即第二个服务器B;
  • 第三个请求发送到再下一个服务器,即第三个服务器C;对于第四个请求,由于三个服务器都已经依次发送过一次请求,所以需要从头开始,先发送到第一个服务器A;
  • 依此类推……

该算法的优点是实现简单且可靠性高。然而,它没有考虑服务器的实际负载情况,可能导致一些服务器承担过重的负载,而其他服务器则处于空闲状态。

下面是轮询算法的一个简单实现代码,让你对其有个大致了解:


        
        
            

          
 public
 
           
          
 
 class
 
  
 
 RoundRobinDemo
 
  
 
          {
            

              
          
 // 定义一个全局计数器,每次调用时递增
 
            

              
          
 private
 
          
 static
 
           AtomicInteger index = 
          
 new
 
           AtomicInteger(-
          
 1
 
          );
            

              
          
 // 定义一个服务器列表
 
            

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

            

              
          
 
 public
 
  
 
 static
 
  String 
 
 roundRobin
 
 
 ()
 
  
 
          {
            

                  
          
 // 获取服务器数量
 
            

                  
          
 int
 
           serverCount = serverList.size();
            

                  
          
 // 确定当前请求应转发到哪个服务器
 
            

                  
          
 int
 
           currentServerIndex = index.incrementAndGet() % serverCount;
            

                  
          
 // 返回相应的服务器地址
 
            

                  
          
 return
 
           serverList.get(currentServerIndex);
            

              }
            

            

              
          
 
 public
 
  
 
 static
 
  
 
 void
 
  
 
 main
 
 
 (String[] args)
 
  
 
          {
            

                  serverList.add(
          
 "Server A"
 
          );
            

                  serverList.add(
          
 "Server B"
 
          );
            

                  serverList.add(
          
 "Server C"
 
          );
            

                  System.out.println(roundRobin());
            

                  System.out.println(roundRobin());
            

                  System.out.println(roundRobin());
            

                  System.out.println(roundRobin());
            

              }
            

          }
            

        
      

输出结果:


        
        
            

          Server A
            

          Server B
            

          Server C
            

          Server A
            

        
      

加权轮询算法(Weighted Round Robin)

加权轮询算法是在轮询算法的基础上进行改进。其思路是在服务器选择过程中,根据服务器的处理能力或负载情况为服务器分配不同的权重,以便处理能力更强或负载较轻的服务器能够接收更多请求。

如下图所示:

picture.image

服务器A、B和C的权重分别为4、3和1。那么服务器A将接收并处理更多请求。可以看到,前三个请求被路由到服务器A,而第四个请求被路由到服务器B。

该算法的优点是可以根据服务器的实际负载情况分配请求。然而,仍然存在服务器负载不均衡的问题,因为它仅根据权重值进行分配,而没有考虑服务器的实际负载情况。

以下是加权轮询算法的示例:


        
        
            

          
 public
 
           
          
 
 class
 
  
 
 WeightRoundRobinDemo
 
  
 
          {
            

              
          
 // 定义一个全局计数器,每次调用时递增
 
            

              
          
 private
 
          
 static
 
           AtomicInteger atomicInteger = 
          
 new
 
           AtomicInteger(
          
 0
 
          );
            

              
          
 // 定义一个服务器及其对应权重值的映射
 
            

              
          
 private
 
          
 static
 
           Map<String, Integer> serverMap = 
          
 new
 
           TreeMap<>();
            

              
          
 // 记录所有服务器的总权重
 
            

              
          
 private
 
          
 static
 
          
 int
 
           totalWeight = 
          
 0
 
          ;
            

            

              
          
 
 public
 
  
 
 static
 
  
 
 void
 
  
 
 main
 
 
 (String[] args)
 
  
 
          {
            

                  serverMap.put(
          
 "Server A"
 
          , 
          
 4
 
          );
            

                  serverMap.put(
          
 "Server B"
 
          , 
          
 3
 
          );
            

                  serverMap.put(
          
 "Server C"
 
          , 
          
 1
 
          );
            

                  
          
 // 计算所有服务器的总权重
 
            

                  
          
 for
 
           (Map.Entry<String, Integer> entry : serverMap.entrySet()) {
            

                      totalWeight += entry.getValue();
            

                  }
            

                  
          
 // 模拟四个请求
 
            

                  System.out.println(weightRoundRobin());
            

                  System.out.println(weightRoundRobin());
            

                  System.out.println(weightRoundRobin());
            

                  System.out.println(weightRoundRobin());
            

              }
            

            

              
          
 
 public
 
  
 
 static
 
  String 
 
 weightRoundRobin
 
 
 ()
 
  
 
          {
            

                  
          
 // 获取服务器数量
 
            

                  
          
 int
 
           serverCount = serverMap.size();
            

                  
          
 // 如果没有可用服务器,返回null
 
            

                  
          
 if
 
           (serverCount == 
          
 0
 
          ) {
            

                      
          
 return
 
          
 null
 
          ;
            

                  }
            

                  
          
 // 为避免多线程环境中并发操作导致的错误,在方法内部执行锁操作
 
            

                  
          
 synchronized
 
           (serverMap) {
            

                      
          
 // 确定当前请求应转发到哪个服务器
 
            

                      
          
 int
 
           currentServerIndex = atomicInteger.incrementAndGet() % totalWeight;
            

                      
          
 // 遍历服务器列表并根据服务器权重值选择相应地址
 
            

                      
          
 for
 
           (Map.Entry<String, Integer> entry : serverMap.entrySet()) {
            

                          String serverAddress = entry.getKey();
            

                          Integer weight = entry.getValue();
            

                          currentServerIndex -= weight;
            

                          
          
 if
 
           (currentServerIndex < 
          
 0
 
          ) {
            

                              
          
 return
 
           serverAddress;
            

                          }
            

                      }
            

                  }
            

                  
          
 return
 
          
 null
 
          ;
            

              }
            

          }
            

        
      

输出结果:


        
        
            

          Server A
            

          Server A
            

          Server A
            

          Server B
            

        
      

随机算法(Random)

随机算法是一种将请求随机分配到后端服务器的负载均衡算法。该算法实现简单,但分配效果不可控,难以确保后端服务器的负载均衡。因此,随机算法通常在测试或压力测试等临时场景中用作负载均衡算法。

如下图所示:

picture.image

  • 第一个请求随机分配到服务器A;
  • 第二个和第四个请求随机分配到服务器C;
  • 第三个请求随机分配到服务器B。

实现随机化方法的代码可以如下:


        
        
            

          
 // 定义一个服务器列表
 
            

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

            

          
 
 public
 
  
 
 static
 
  String 
 
 random
 
 
 ()
 
  
 
          {
            

              
          
 // 获取服务器数量
 
            

              
          
 int
 
           serverCount = serverList.size();
            

              
          
 // 如果没有可用服务器,返回null
 
            

              
          
 if
 
           (serverCount == 
          
 0
 
          ) {
            

                  
          
 return
 
          
 null
 
          ;
            

              }
            

              
          
 // 生成一个随机数
 
            

              
          
 int
 
           randomIndex = 
          
 new
 
           Random().nextInt(serverCount);
            

              
          
 // 返回相应的服务器地址
 
            

              
          
 return
 
           serverList.get(randomIndex);
            

          }
            

        
      

加权随机算法(Weighted Random)

加权随机算法是在随机算法的基础上进行改进。其思路是在服务器选择过程中,根据服务器的处理能力或负载情况为服务器分配不同的权重,以便处理能力更强或负载较轻的服务器能够获得更多请求。

picture.image

加权随机算法的实现代码如下:


        
        
            

          
 // 定义一个服务器及其对应权重值的映射
 
            

          
 private
 
          
 static
 
           Map<String, Integer> serverMap = 
          
 new
 
           ConcurrentHashMap<>();
            

            

          
 
 public
 
  
 
 static
 
  String 
 
 weightRandom
 
 
 ()
 
  
 
          {
            

              
          
 // 获取服务器数量
 
            

              
          
 int
 
           serverCount = serverMap.size();
            

              
          
 // 如果没有可用服务器,返回null
 
            

              
          
 if
 
           (serverCount == 
          
 0
 
          ) {
            

                  
          
 return
 
          
 null
 
          ;
            

              }
            

              
          
 // 为避免多线程环境中并发操作导致的错误,在方法内部执行锁操作
 
            

              
          
 synchronized
 
           (serverMap) {
            

                  
          
 // 计算所有服务器的总权重
 
            

                  
          
 int
 
           totalWeight = 
          
 0
 
          ;
            

                  
          
 for
 
           (Map.Entry<String, Integer> entry : serverMap.entrySet()) {
            

                      totalWeight += entry.getValue();
            

                  }
            

              }
            

              
          
 // 生成一个随机数
 
            

              
          
 int
 
           randomWeight = 
          
 new
 
           Random().nextInt(totalWeight);
            

              
          
 // 遍历服务器列表并根据服务器权重值选择相应地址
 
            

              
          
 for
 
           (Map.Entry<String, Integer> entry : serverMap.entrySet()) {
            

                  String serverAddress = entry.getKey();
            

                  Integer weight = entry.getValue();
            

                  randomWeight -= weight;
            

                  
          
 if
 
           (randomWeight < 
          
 0
 
          ) {
            

                      
          
 return
 
           serverAddress;
            

                  }
            

              }
            

              
          
 // 默认返回null
 
            

              
          
 return
 
          
 null
 
          ;
            

          }
            

        
      

源IP哈希算法(Hash)

源IP哈希算法是一种基于请求源IP地址的负载均衡算法。其思路是通过哈希函数为每个请求的源IP地址计算一个值,然后根据该值与可用服务器总数取模的结果来确定请求应转发到哪个服务器。

换句话说,源IP哈希算法使用客户端IP地址作为哈希键。负载均衡器将哈希值映射到可用服务器之一,然后将请求发送到该服务器进行处理。如果客户端IP地址发生变化(例如,重启后重新分配了新的IP地址),那么它将被分配到另一个服务器。

如下图所示:

picture.image

来自客户端A的所有请求都分配到服务器A;来自客户端B的所有请求都分配到服务器C。

该算法的优点是可以避免某些客户端被重定向到不同的服务器。来自同一IP地址的请求将始终分配到同一服务器,因此可以在一定程度上提高缓存命中率等性能指标。然而,它也有一些缺点。例如,如果来自同一IP地址的请求很多,可能会导致某个服务器负载过重。

此外,由于服务器数量的变化,哈希值映射也会改变,这可能导致缓存失效,需要重新分配所有请求。

源IP哈希算法的实现代码示例如下:


        
        
            

          
 // 定义一个服务器列表
 
            

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

            

          
 
 public
 
  
 
 static
 
  String 
 
 hash
 
 
 (String clientIP)
 
  
 
          {
            

              
          
 // 获取服务器数量
 
            

              
          
 int
 
           serverCount = serverList.size();
            

              
          
 // 如果没有可用服务器,返回null
 
            

              
          
 if
 
           (serverCount == 
          
 0
 
          ) {
            

                  
          
 return
 
          
 null
 
          ;
            

              }
            

              
          
 // 计算客户端IP地址的哈希码
 
            

              
          
 int
 
           hashCode = clientIP.hashCode();
            

              
          
 // 根据哈希码确定请求应转发到哪个服务器
 
            

              
          
 int
 
           serverIndex = hashCode % serverCount;
            

              
          
 // 返回相应的服务器地址
 
            

              
          
 return
 
           serverList.get(serverIndex);
            

          }
            

        
      

最少连接算法(Least Connections)

最少连接算法是一种动态调整的负载均衡算法。其思路是尽可能将请求分配到当前空闲连接数最少的后端服务器,以达到负载均衡的效果。在实现过程中,通常需要定期检测每个服务器的连接数并进行动态调整。

如下图所示:

picture.image

由于服务器C当前的连接数最少,所有请求都将分配给它。

最少连接算法的实现代码示例如下:


        
        
            

          
 // 定义一个服务器列表
 
            

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

          
 // 记录每个服务器的连接数
 
            

          
 private
 
          
 static
 
           Map<String, Integer> connectionsMap = 
          
 new
 
           ConcurrentHashMap<>();
            

            

          
 
 public
 
  
 
 static
 
  String 
 
 leastConnections
 
 
 ()
 
  
 
          {
            

              
          
 // 获取服务器数量
 
            

              
          
 int
 
           serverCount = serverList.size();
            

              
          
 // 如果没有可用服务器,返回null
 
            

              
          
 if
 
           (serverCount == 
          
 0
 
          ) {
            

                  
          
 return
 
          
 null
 
          ;
            

              }
            

              
          
 // 默认选择第一个服务器
 
            

              String selectedServerAddress = serverList.get(
          
 0
 
          );
            

              
          
 // 获取第一个服务器的连接数
 
            

              
          
 int
 
           minConnections = connectionsMap.getOrDefault(selectedServerAddress, 
          
 0
 
          );
            

              
          
 // 遍历服务器列表以找到连接数最少的服务器
 
            

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

                  String serverAddress = serverList.get(i);
            

                  
          
 int
 
           connections = connectionsMap.getOrDefault(serverAddress, 
          
 0
 
          );
            

                  
          
 if
 
           (connections < minConnections) {
            

                      selectedServerAddress = serverAddress;
            

                      minConnections = connections;
            

                  }
            

              }
            

              
          
 // 返回连接数最少的服务器的地址
 
            

              
          
 return
 
           selectedServerAddress;
            

          }
            

        
      

需要注意的是,如果服务器宕机或网络链路中断,负载均衡器将需要重新计算服务器的连接数,这将延长响应时间并影响性能。

好了,这次分享就到这里,下次再见!

最后欢迎 加入苏三的星球 ,你将获得:苏三AI项目、 商城微服务实战、秒杀系统实战 、 商城系统实战、秒杀系统实战、代码生成工具、系统设计、性能优化、技术选型、底层原理、Spring源码解读、工作经验分享、痛点问题、面试八股文等多个优质专栏。

还有1V1答疑、修改简历、职业规划、送书活动、技术交流。

picture.image

目前星球已经更新了5200+篇优质内容,还在持续爆肝中.....

星球已经被官方推荐了3次,收到了小伙伴们的一致好评。戳我加入学习,已有1600+小伙伴加入学习。

picture.image

0
0
0
0
关于作者
关于作者

文章

0

获赞

0

收藏

0

相关资源
云原生机器学习系统落地和实践
机器学习在字节跳动有着丰富业务场景:推广搜、CV/NLP/Speech 等。业务规模的不断增大对机器学习系统从用户体验、训练效率、编排调度、资源利用等方面也提出了新的挑战,而 Kubernetes 云原生理念的提出正是为了应对这些挑战。本次分享将主要介绍字节跳动机器学习系统云原生化的落地和实践。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论