编辑
2026-05-10
记录知识
0

目录

前言
负载均衡算法的本质
负载均衡算法分类
1. 轮询算法(Round Robin)
2. 加权轮询算法(Weighted Round Robin)
3. 最少连接算法(Least Connections)
4. 随机算法(Random)
5. 源地址哈希算法(Source Hash)
算法对比与选择
总结
参考资料

前言

当清晨的第一缕阳光洒向大地,城市的交通网络开始繁忙起来。在十字路口红绿灯的指挥下,车辆有序地穿梭流动。然而,如果红绿灯的调度策略不合理——比如东西方向明明没有车辆通行却让车辆等待,而南北方向已经排起长龙却迟迟不放行——整座城市将陷入瘫痪。

负载均衡器就像是整个系统架构中的"红绿灯",它决定了请求应该被路由到哪台服务器去处理。而负载均衡算法,就是这套调度系统的核心逻辑。选择合适的负载均衡算法,能让系统资源物尽其用、用户请求得到快速响应;选择错误的算法,则可能导致部分服务器过载、其他服务器闲置,整体性能大打折扣。

这便是我们今天要深入探讨的主题——高性能负载均衡的核心算法。


负载均衡算法的本质

在前文中我们已经了解到,负载均衡器负责将请求分发到多台服务器。而具体如何选择目标服务器,就需要依靠负载均衡算法来实现。

负载均衡算法并非孤立存在,它服务于整体的系统目标。有的算法追求绝对的公平——每个请求都依次分发,让每台服务器获得均等的处理机会;有的算法追求效率——根据服务器的实际负载动态调整分发策略;还有的算法追求稳定——确保同一用户的请求始终被路由到同一台服务器,保证会话的连续性。

不同的算法各有其适用场景,没有放之四海而皆准的最优解。作为架构师,需要深入理解每种算法的特性,才能在实际设计中做出正确的选择。


负载均衡算法分类

1. 轮询算法(Round Robin)

轮询算法是最简单、最直观的负载均衡策略。其核心思想是:将所有请求依次分发到每台服务器,周而复始,形成一个环形的调度队列。

工作原理: 假设有3台服务器A、B、C,第1个请求发往A,第2个发往B,第3个发往C,第4个又回到A……如此循环往复。

优点

  • 实现简单,易于理解和实现
  • 对所有服务器一视同仁,完全公平
  • 无需关心服务器的实际状态(如CPU、内存使用率)

缺点

  • 不考虑服务器性能差异——将请求发给性能较弱的服务器和性能强劲的服务器,被认为是等价的
  • 不考虑服务器当前负载——即使某台服务器已经过载,仍可能收到新请求
  • 适合场景:服务器硬件配置相同、业务请求类型一致的环境

Python实现

python
import threading import time class RoundRobinLB: """轮询负载均衡器""" def __init__(self, servers): self.servers = servers self.current_index = 0 self.lock = threading.Lock() def select_server(self): with self.lock: server = self.servers[self.current_index] self.current_index = (self.current_index + 1) % len(self.servers) return server def dispatch(self, request_id): server = self.select_server() print(f"[轮询] 请求#{request_id} -> 服务器 {server}") # 测试 if __name__ == "__main__": servers = ["192.168.1.101", "192.168.1.102", "192.168.1.103"] lb = RoundRobinLB(servers) print("=" * 50) print("[测试] 轮询算法 - 10个请求的分发") print("=" * 50) for i in range(10): lb.dispatch(i) print("\n" + "=" * 50) print("[分析] 轮询算法特点:") print("- 请求#0,3,6,9 分发到 192.168.1.101") print("- 请求#1,4,7 分发到 192.168.1.102") print("- 请求#2,5,8 分发到 192.168.1.103") print("每台服务器恰好分担了3-4个请求,实现了公平分配")

预期输出

================================================== [测试] 轮询算法 - 10个请求的分发 ================================================== [轮询] 请求#0 -> 服务器 192.168.1.101 [轮询] 请求#1 -> 服务器 192.168.1.102 [轮询] 请求#2 -> 服务器 192.168.1.103 [轮询] 请求#3 -> 服务器 192.168.1.101 [轮询] 请求#4 -> 服务器 192.168.1.102 [轮询] 请求#5 -> 服务器 192.168.1.103 [轮询] 请求#6 -> 服务器 192.168.1.101 [轮询] 请求#7 -> 服务器 192.168.1.102 [轮询] 请求#8 -> 服务器 192.168.1.103 [轮询] 请求#9 -> 服务器 192.168.1.101 ================================================== [分析] 轮询算法特点: - 请求#0,3,6,9 分发到 192.168.1.101 - 请求#1,4,7 分发到 192.168.1.102 - 请求#2,5,8 分发到 192.168.1.103 每台服务器恰好分担了3-4个请求,实现了公平分配

2. 加权轮询算法(Weighted Round Robin)

轮询算法假设所有服务器具有相同的处理能力,但在实际环境中,不同的服务器往往有着不同的硬件配置——有的服务器配备高性能CPU和大容量内存,有的则是老旧的设备。如果继续使用简单的轮询算法,显然无法充分利用高性能服务器的能力,造成资源浪费。

加权轮询正是为了解决这个问题而设计的。它允许为每台服务器分配一个"权重",权重越高的服务器能够处理更多的请求。

工作原理: 假设有3台服务器A、B、C,权重分别为5、3、2。这意味着A应该处理5个请求,B处理3个,C处理2个,如此循环。

权重为5的服务器,其处理能力可能是权重为2的服务器的2.5倍。通过加权策略,我们可以让高性能服务器承担更多负载,同时不会让低性能服务器超载。

Python实现

python
import random class WeightedRoundRobin: """加权轮询负载均衡器""" def __init__(self, servers_with_weights): # servers_with_weights: [(server, weight), ...] self.servers = servers_with_weights self.current_weight_index = 0 self.current_sub_index = 0 # 构建展开后的调度队列 self.dispatch_queue = [] for server, weight in servers_with_weights: self.dispatch_queue.extend([server] * weight) self.queue_size = len(self.dispatch_queue) print(f"[加权轮询] 初始化完成,权重配置: {dict(servers_with_weights)}") print(f"[加权轮询] 调度队列: {self.dispatch_queue}") def select_server(self): server = self.dispatch_queue[self.current_sub_index] self.current_sub_index = (self.current_sub_index + 1) % self.queue_size return server def dispatch(self, request_id): server = self.select_server() print(f"[加权轮询] 请求#{request_id} -> 服务器 {server}") # 测试 if __name__ == "__main__": servers_weights = [ ("192.168.1.101", 5), # 高性能服务器 ("192.168.1.102", 3), # 中等性能 ("192.168.1.103", 2), # 低性能 ] lb = WeightedRoundRobin(servers_weights) print("\n" + "=" * 50) print("[测试] 加权轮询算法 - 20个请求的分发") print("=" * 50) # 统计每台服务器接收的请求数 stats = {"192.168.1.101": 0, "192.168.1.102": 0, "192.168.1.103": 0} for i in range(20): server = lb.select_server() stats[server] += 1 lb.dispatch(i) print("\n" + "=" * 50) print("[统计] 各服务器接收请求数:") for server, count in stats.items(): print(f" {server}: {count} 个请求") print(f" 比例接近 5:3:2(权重比例)")

预期输出

[加权轮询] 初始化完成,权重配置: {'192.168.1.101': 5, '192.168.1.102': 3, '192.168.1.103': 2} [加权轮询] 调度队列: ['192.168.1.101', '192.168.1.101', '192.168.1.101', '192.168.1.101', '192.168.1.101', '192.168.1.102', '192.168.1.102', '192.168.1.102', '192.168.1.103', '192.168.1.103'] ================================================== [测试] 加权轮询算法 - 20个请求的分发 ================================================== [加权轮询] 请求#0 -> 服务器 192.168.1.101 [加权轮询] 请求#1 -> 服务器 192.168.1.101 [加权轮询] 请求#2 -> 服务器 192.168.1.102 [加权轮询] 请求#3 -> 服务器 192.168.1.103 ... ================================================== [统计] 各服务器接收请求数: 192.168.1.101: 约10个请求(占比50%) 192.168.1.102: 约6个请求(占比30%) 192.168.1.103: 约4个请求(占比20%) 比例接近 5:3:2(权重比例)

3. 最少连接算法(Least Connections)

轮询和加权轮询算法都基于"请求数量"来分配任务,但在实际场景中,不同请求可能消耗差异巨大的服务器资源。例如,一个简单的静态页面请求可能只需要几毫秒就能完成,而一个复杂的数据库查询可能需要数秒。

最少连接算法的核心思想是:将请求发送到当前活跃连接数最少的服务器。这样,新请求会被分发到相对"空闲"的服务器,避免出现某些服务器已经堆积了大量慢请求而其他服务器却无所事事的情况。

工作原理: 当某台服务器处理请求时,其"活动连接数"加1;当请求处理完毕时,活动连接数减1。负载均衡器始终选择活动连接数最少的那台服务器。

Python实现

python
import threading import time import random class Server: def __init__(self, ip, weight=1): self.ip = ip self.weight = weight self.active_connections = 0 self.lock = threading.Lock() def __str__(self): return f"{self.ip}(活跃:{self.active_connections})" class LeastConnectionsLB: """最少连接负载均衡器""" def __init__(self, servers): self.servers = servers def select_server(self): # 选择活跃连接数最少的服务器 min_conn_server = min(self.servers, key=lambda s: s.active_connections) return min_conn_server def dispatch(self, request_id): server = self.select_server() with server.lock: server.active_connections += 1 print(f"[最少连接] 请求#{request_id} -> {server}") # 模拟请求处理 processing_time = random.uniform(0.1, 0.5) threading.Thread(target=self.release_connection, args=(server, processing_time)).start() return server def release_connection(self, server, delay): time.sleep(delay) with server.lock: server.active_connections -= 1 print(f"[释放] 服务器 {server.ip} 空闲,活跃连接数: {server.active_connections}") # 测试 if __name__ == "__main__": servers = [ Server("192.168.1.101"), Server("192.168.1.102"), Server("192.168.1.103"), ] lb = LeastConnectionsLB(servers) print("=" * 50) print("[测试] 最少连接算法 - 模拟并发请求") print("=" * 50) # 模拟多个请求同时到达 threads = [] for i in range(8): t = threading.Thread(target=lambda req_id: lb.dispatch(req_id), args=(i,)) threads.append(t) t.start() time.sleep(0.05) # 模拟请求陆续到达 for t in threads: t.join() time.sleep(1) # 等待连接释放 print("\n[最终状态] 所有连接已释放")

预期输出

================================================== [测试] 最少连接算法 - 模拟并发请求 ================================================== [最少连接] 请求#0 -> 192.168.1.101(活跃:1) [最少连接] 请求#1 -> 192.168.1.102(活跃:1) [最少连接] 请求#2 -> 192.168.1.103(活跃:1) [最少连接] 请求#3 -> 192.168.1.101(活跃:2) [因为其活跃数最少] [最少连接] 请求#4 -> 192.168.1.102(活跃:2) [同样是最少] [最少连接] 请求#5 -> 192.168.1.103(活跃:2) [同样是最少] [最少连接] 请求#6 -> 192.168.1.101(活跃:3) [继续选择活跃数最少的] ... [释放] 服务器 192.168.1.101 空闲,活跃连接数: 0 ...

4. 随机算法(Random)

顾名思义,随机算法将请求随机分发到任意一台服务器。从概率角度来看,如果服务器数量足够多,随机算法能够将请求较为均匀地分配到各台服务器。

随机算法在实现上极其简单,不需要维护任何状态信息。但它无法保证分配的均匀性——极端情况下,可能连续多个请求都被发送到同一台服务器。

加权随机算法(Weighted Random)是随机算法的升级版,根据服务器权重进行随机选择,权重越大的服务器被选中的概率越高。

Python实现

python
import random class RandomLB: """随机负载均衡器""" def __init__(self, servers): self.servers = servers def dispatch(self, request_id): server = random.choice(self.servers) print(f"[随机] 请求#{request_id} -> 服务器 {server}") class WeightedRandomLB: """加权随机负载均衡器""" def __init__(self, servers_with_weights): self.servers = [s for s, w in servers_with_weights] self.weights = [w for s, w in servers_with_weights] self.total_weight = sum(self.weights) def select_server(self): rand = random.uniform(0, self.total_weight) cumulative = 0 for i, w in enumerate(self.weights): cumulative += w if rand <= cumulative: return self.servers[i] return self.servers[-1] def dispatch(self, request_id): server = self.select_server() print(f"[加权随机] 请求#{request_id} -> 服务器 {server}") # 测试 if __name__ == "__main__": print("=" * 50) print("[测试] 随机算法 vs 加权随机算法") print("=" * 50) # 测试普通随机 print("\n[普通随机] 10次选择:") servers = ["A", "B", "C"] lb = RandomLB(servers) for i in range(10): lb.dispatch(i) # 测试加权随机 print("\n[加权随机] 20次选择 (权重 A:B:C = 5:3:2):") servers_weights = [("A", 5), ("B", 3), ("C", 2)] lb_weighted = WeightedRandomLB(servers_weights) stats = {"A": 0, "B": 0, "C": 0} for i in range(20): server = lb_weighted.select_server() stats[server] += 1 print(f"[统计] A:{stats['A']}次, B:{stats['B']}次, C:{stats['C']}次") print("A被选中的概率最高(约50%),C被选中的概率最低(约20%)")

5. 源地址哈希算法(Source Hash)

源地址哈希算法根据请求来源的IP地址计算哈希值,然后根据哈希值选择服务器。相同来源的请求将始终被路由到同一台服务器,这对于需要保持会话一致性的场景至关重要。

工作原理

  • 计算请求IP的哈希值:hash(request_ip)
  • 使用哈希值对服务器数量取模:server_index = hash(ip) % server_count
  • 将请求发送到计算得到的服务器

应用场景

  • Session保持:用户登录后,后续请求需要访问同一台服务器以获取session数据
  • 缓存友好:相同资源的请求路由到同一台服务器,提高缓存命中率

Python实现

python
import hashlib class SourceHashLB: """源地址哈希负载均衡器""" def __init__(self, servers): self.servers = servers self.server_count = len(servers) def select_server(self, client_ip): # 使用MD5计算哈希(实际应用中可用其他算法) hash_val = int(hashlib.md5(client_ip.encode()).hexdigest(), 16) server_index = hash_val % self.server_count return self.servers[server_index] def dispatch(self, request_id, client_ip): server = self.select_server(client_ip) print(f"[源地址哈希] 请求#{request_id} 来源IP:{client_ip} -> {server}") # 测试 if __name__ == "__main__": servers = ["Server-A", "Server-B", "Server-C"] lb = SourceHashLB(servers) print("=" * 50) print("[测试] 源地址哈希算法 - Session保持") print("=" * 50) # 模拟不同用户的请求 test_ips = [ "192.168.1.100", # 用户1 "192.168.1.101", # 用户2 "192.168.1.100", # 用户1的第二个请求 "192.168.1.102", # 用户3 "192.168.1.101", # 用户2的第二个请求 ] print("\n模拟场景:用户访问需要登录的页面") for i, ip in enumerate(test_ips): lb.dispatch(i, ip) print("\n[分析] 相同IP的请求被路由到同一台服务器:") print(" 192.168.1.100 -> 全部路由到同一台(实现Session保持)") print(" 192.168.1.101 -> 全部路由到同一台") print(" 不同IP可能被路由到不同服务器")

预期输出

================================================== [测试] 源地址哈希算法 - Session保持 ================================================== 模拟场景:用户访问需要登录的页面 [源地址哈希] 请求#0 来源IP:192.168.1.100 -> Server-A [源地址哈希] 请求#1 来源IP:192.168.1.101 -> Server-B [源地址哈希] 请求#2 来源IP:192.168.1.100 -> Server-A [与请求#0相同!] [源地址哈希] 请求#3 来源IP:192.168.1.102 -> Server-C [源地址哈希] 请求#4 来源IP:192.168.1.101 -> Server-B [与请求#1相同!] [分析] 相同IP的请求被路由到同一台服务器: 192.168.1.100 -> 全部路由到同一台(实现Session保持) 192.168.1.101 -> 全部路由到同一台 不同IP可能被路由到不同服务器

算法对比与选择

算法核心思想优点缺点适用场景
轮询依次分配简单、公平不考虑性能差异服务器性能一致
加权轮询按权重分配充分利用高性能服务器需要手动配置权重服务器性能不均
最少连接选择空闲服务器动态适应负载变化实现复杂、开销较大请求处理时间差异大
随机随机选择实现简单不保证均匀性服务器数量多时近似公平
源地址哈希同源同服务器实现会话保持可能导致负载不均Session保持、缓存场景

实际应用中的考虑

  • Nginx默认使用加权轮询算法
  • LVS支持多种算法,包括轮询、最少连接、基于局部性的最少连接等
  • 分布式缓存(如Redis Cluster)使用哈希槽来分配数据,本质上也是哈希算法的一种应用

总结

  • 负载均衡算法是决定请求分发策略的核心组件,直接影响系统性能和稳定性
  • 轮询算法实现简单、公平,但不考虑服务器性能差异
  • 加权轮询算法根据服务器权重分配请求,充分利用高性能服务器资源
  • 最少连接算法动态选择当前负载最低的服务器,适应请求处理时间差异大的场景
  • 随机算法实现简单,在服务器数量足够多时可近似均匀分配
  • 源地址哈希算法保证相同来源请求路由到同一服务器,适用于会话保持场景
  • 没有最优的算法,只有最适合场景的算法,需要根据业务特点进行选择
  • 实际系统中,负载均衡算法通常需要配合健康检测机制,确保请求不会被分发到故障服务器
  • 大型分布式系统可能采用复合算法,结合多种策略的优势来优化整体性能

参考资料

  • 《软件架构基础》