六种负载均衡算法

2025/11/12 算法

负载均衡(Load Balancing)是一种计算机网络和服务器管理技术,旨在分配网络流量、请求或工作负载到多个服务器或资源,以确保这些服务器能够高效、均匀地处理负载,并且能够提供更高的性能、可用性和可扩展性。

# 1. 轮询 (Round Robin)

轮询算法将请求按顺序分配给每个服务器,就像循环队列一样。第一个请求分配给第一个服务器,第二个请求分配给第二个服务器,直到所有服务器都分配到了请求,然后再次从第一个服务器开始。

适用场景

  • 所有服务器硬件配置相同
  • 每个请求的处理时间差不多
  • 不需要保持会话状态

优缺点

  • 优点:简单、公平
  • 缺点:不考虑服务器性能差异和当前负载

# Nginx实现

http {
    upstream backend {
        # 默认就是轮询,无需特殊指令
        server 192.168.1.10:8080;
        server 192.168.1.11:8080;
        server 192.168.1.12:8080;
    }
    
    server {
        listen 80;
        location / {
            proxy_pass http://backend;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 代码实现

public class RoundRobinLoadBalancer {
    private List<String> servers;
    private AtomicInteger currentIndex = new AtomicInteger(0);
    
    public RoundRobinLoadBalancer(List<String> servers) {
        this.servers = servers;
    }
    
    public String getServer() {
        int index = currentIndex.getAndIncrement() % servers.size();
        if (index < 0) {
            index = Math.abs(index);
        }
        return servers.get(index);
    }
}

// 使用示例
List<String> servers = Arrays.asList("server1:8080", "server2:8080", "server3:8080");
RoundRobinLoadBalancer lb = new RoundRobinLoadBalancer(servers);
// 每次调用轮换服务器
String selectedServer = lb.getServer();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 2. 粘性轮询 (Sticky Round-Robin)

粘性轮询是标准轮询算法的一个变种,它通过记住客户端与服务实例的映射关系,确保来自同一客户端的连续请求会被路由到同一个服务实例上。

特点

  • 会话保持:一旦客户端首次请求被分配到某个服务实例,后续请求会"粘"在这个实例上
  • 客户端识别:通常基于客户端IP、会话ID或特定HTTP头来识别客户端
  • 故障转移:当目标服务实例不可用时,系统会重新分配客户端到其他可用实例

适用场景

  • 需要会话保持(session affinity)的应用,比如购物车、用户登录状态等。
  • 文件上传等需要连续连接的业务

# Nginx实现

upstream backend {
    # 基于IP的粘性会话
    ip_hash;
    
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
}

# 或者基于cookie
upstream backend {
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
}

server {
    listen 80;
    location / {
        # 基于cookie的粘性会话
        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        
        # 添加cookie用于会话保持
        proxy_cookie_path / "/; sticky";
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 代码实现

@Configuration
public class LoadBalancerConfig {
    
    @Bean
    public ServiceInstanceListSupplier stickySessionSupplier(
            ConfigurableApplicationContext context) {
        return ServiceInstanceListSupplier.builder()
                .withBlockingDiscoveryClient()
                .withCaching()
                .withStickySessions()  // 启用粘性会话
                .build(context);
    }
}

// 或者在代码中手动实现
@Component
public class StickySessionLoadBalancer {
    private final List<String> servers = Arrays.asList("server1", "server2", "server3");
    private final Map<String, String> sessionMap = new ConcurrentHashMap<>();
    
    public String getServer(String sessionId) {
        return sessionMap.computeIfAbsent(sessionId, 
            key -> servers.get(Math.abs(key.hashCode()) % servers.size()));
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 3. 加权轮询 (Weighted Round-Robin)

加权轮询是标准轮询算法的增强版本,它允许管理员为每个服务实例分配不同的权重值。权重越高的实例处理越多的请求,从而实现更精细的负载分配。

特点

  • 权重分配:每个服务实例都有对应的权重值

  • 比例分配:请求按权重比例分配到不同实例

  • 动态调整:权重可以动态修改以适应不同场景

适用场景

  • 服务器配置不均,希望性能好的服务器处理更多请求。

# Nginx实现

upstream backend {
    server 192.168.1.10:8080 weight=5;  # 高性能服务器
    server 192.168.1.11:8080 weight=3;  # 中等性能
    server 192.168.1.12:8080 weight=1;  # 低性能服务器
    server 192.168.1.13:8080 weight=0;  # 备份服务器,不参与负载
}
1
2
3
4
5
6

# 代码实现(平滑加权轮询算法)

public class WeightedRoundRobinLoadBalancer {
    private List<Server> servers;
    
    static class Server {
        String address;
        int weight;          // 配置权重
        int currentWeight;   // 当前权重
        
        public Server(String address, int weight) {
            this.address = address;
            this.weight = weight;
            this.currentWeight = 0;
        }
    }
    
    public WeightedRoundRobinLoadBalancer(Map<String, Integer> serverWeights) {
        this.servers = new ArrayList<>();
        serverWeights.forEach((address, weight) -> {
            servers.add(new Server(address, weight));
        });
    }
    
    public String getServer() {
        // 选择当前权重最大的服务器
        Server selected = null;
        int totalWeight = 0;
        
        for (Server server : servers) {
            server.currentWeight += server.weight;
            totalWeight += server.weight;
            
            if (selected == null || server.currentWeight > selected.currentWeight) {
                selected = server;
            }
        }
        
        if (selected != null) {
            selected.currentWeight -= totalWeight;
            return selected.address;
        }
        
        return null;
    }
}

// 使用示例
Map<String, Integer> serverWeights = new HashMap<>();
serverWeights.put("server1:8080", 5);
serverWeights.put("server2:8080", 3);
serverWeights.put("server3:8080", 1);

WeightedRoundRobinLoadBalancer lb = new WeightedRoundRobinLoadBalancer(serverWeights);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

# 4. 源地址哈希法 (Hash)

对客户端的IP地址(或其他标识)进行哈希计算,然后根据哈希值选择服务器。这样可以保证同一个客户端的请求总是被分配到同一台服务器。

适用场景

  • 需要会话保持,且不使用粘性轮询的场合。
  • 希望将特定客户端的请求固定到同一台服务器。

# Nginx实现

upstream backend {
    hash $remote_addr;  # 基于客户端IP哈希
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
}

# 或者基于其他变量哈希
upstream backend {
    hash $request_uri;  # 基于请求URI哈希
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 代码实现

public class HashLoadBalancer {
    private List<String> servers;
    
    public HashLoadBalancer(List<String> servers) {
        this.servers = servers;
    }
    
    public String getServer(String key) {
        if (servers.isEmpty()) {
            return null;
        }
        
        int hashCode = Math.abs(key.hashCode());
        int index = hashCode % servers.size();
        return servers.get(index);
    }
    
    // 一致性哈希实现(减少服务器变动时的影响)
    public static class ConsistentHashLoadBalancer {
        private final SortedMap<Integer, String> circle = new TreeMap<>();
        private final int virtualNodes; // 虚拟节点数
        
        public ConsistentHashLoadBalancer(List<String> servers, int virtualNodes) {
            this.virtualNodes = virtualNodes;
            for (String server : servers) {
                addServer(server);
            }
        }
        
        private void addServer(String server) {
            for (int i = 0; i < virtualNodes; i++) {
                String virtualNode = server + "#" + i;
                int hash = getHash(virtualNode);
                circle.put(hash, server);
            }
        }
        
        public String getServer(String key) {
            if (circle.isEmpty()) {
                return null;
            }
            
            int hash = getHash(key);
            SortedMap<Integer, String> tailMap = circle.tailMap(hash);
            int nodeHash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
            return circle.get(nodeHash);
        }
        
        private int getHash(String key) {
            // 使用MD5哈希,也可以使用其他哈希算法
            return Math.abs(key.hashCode());
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

# 5. 最少连接 (Least Connections)

最少连接算法是一种动态负载均衡策略,它会将新请求分配给当前连接数最少的服务器,以实现更均衡的服务器负载分配。

特点

  • 实时监控:跟踪每台服务器的活跃连接数
  • 动态决策:新请求总是分配给当前连接数最少的服务器
  • 自适应:自动适应不同请求处理能力的服务器

适用场景

  • 长连接应用,例如数据库连接、实时通信等。
  • 当服务器处理能力不同,但已经用权重调整了连接数分配时,可以使用加权最少连接。

# Nginx实现

upstream backend {
    least_conn;  # 最少连接算法
    
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
    
    # 也可以结合权重
    server 192.168.1.13:8080 weight=3;
}
1
2
3
4
5
6
7
8
9
10

# 代码实现

public class LeastConnectionsLoadBalancer {
    private List<ServerStats> servers;
    
    static class ServerStats {
        String address;
        AtomicInteger activeConnections = new AtomicInteger(0);
        int weight;  // 可选:加权最少连接
        
        public ServerStats(String address, int weight) {
            this.address = address;
            this.weight = weight;
        }
        
        public double getScore() {
            // 考虑权重的评分,权重越高,能承受的连接数越多
            return (double) activeConnections.get() / weight;
        }
    }
    
    public LeastConnectionsLoadBalancer(List<String> servers) {
        this.servers = new ArrayList<>();
        for (String server : servers) {
            this.servers.add(new ServerStats(server, 1));
        }
    }
    
    public String getServer() {
        ServerStats selected = null;
        double minScore = Double.MAX_VALUE;
        
        for (ServerStats server : servers) {
            double score = server.getScore();
            if (score < minScore) {
                minScore = score;
                selected = server;
            }
        }
        
        if (selected != null) {
            selected.activeConnections.incrementAndGet();
            return selected.address;
        }
        
        return null;
    }
    
    public void releaseConnection(String serverAddress) {
        for (ServerStats server : servers) {
            if (server.address.equals(serverAddress)) {
                server.activeConnections.decrementAndGet();
                break;
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

# 6. 最快响应时间 (Least Response Time)

最快响应时间(Least Response Time,LRT)通过选择当前响应时间最短的服务器来处理新请求,从而优化整体系统性能。这需要负载均衡器记录每个服务器的历史响应时间。

LRT 算法基于以下核心判断标准:

  • 实时性能监控:持续跟踪每台服务器的历史响应时间
  • 动态路由决策:新请求总是分配给响应最快的可用服务器
  • 自适应学习:根据服务器性能变化自动调整流量分配

适用场景

  • 对响应时间敏感的应用。
  • 服务器性能差异较大,且希望客户端总是连接到响应最快的服务器。

# Nginx Plus实现

upstream backend {
    least_time last_byte;  # 基于完整响应时间
    # least_time header;   # 基于首字节时间
    
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
}
1
2
3
4
5
6
7
8

# 代码实现

public class LeastResponseTimeLoadBalancer {
    private List<ServerMetrics> servers;
    private final double alpha; // 指数平滑系数
    
    static class ServerMetrics {
        String address;
        AtomicLong totalRequests = new AtomicLong(0);
        AtomicDouble averageResponseTime = new AtomicDouble(100.0); // 初始值
        int weight;
        
        public ServerMetrics(String address, int weight) {
            this.address = address;
            this.weight = weight;
        }
        
        public double getScore() {
            // 响应时间越短,分数越低
            return averageResponseTime.get() / weight;
        }
        
        public void updateResponseTime(long responseTime) {
            long count = totalRequests.incrementAndGet();
            double currentAvg = averageResponseTime.get();
            double newAvg = currentAvg + (responseTime - currentAvg) / Math.min(count, 100);
            averageResponseTime.set(newAvg);
        }
    }
    
    public LeastResponseTimeLoadBalancer(List<String> servers, double alpha) {
        this.alpha = alpha;
        this.servers = new ArrayList<>();
        for (String server : servers) {
            this.servers.add(new ServerMetrics(server, 1));
        }
    }
    
    public String getServer() {
        ServerMetrics selected = null;
        double bestScore = Double.MAX_VALUE;
        
        for (ServerMetrics server : servers) {
            double score = server.getScore();
            if (score < bestScore) {
                bestScore = score;
                selected = server;
            }
        }
        
        return selected != null ? selected.address : null;
    }
    
    public void recordResponseTime(String serverAddress, long responseTimeMs) {
        for (ServerMetrics server : servers) {
            if (server.address.equals(serverAddress)) {
                server.updateResponseTime(responseTimeMs);
                break;
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

# 微服务架构中的客户端负载均衡

# Spring Cloud LoadBalancer配置
spring:
  cloud:
    loadbalancer:
      enabled: true
    nacos:
      discovery:
        server-addr: localhost:8848
1
2
3
4
5
6
7
8
// 配置不同的负载均衡策略
@Configuration
public class LoadBalancerConfiguration {
    
    @Bean
    @LoadBalancerClient(name = "user-service", configuration = CustomLoadBalancerConfig.class)
    public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier() {
        return new DiscoveryClientServiceInstanceListSupplier();
    }
}

// 自定义负载均衡配置
public class CustomLoadBalancerConfig {
    @Bean
    public ReactorLoadBalancer<ServiceInstance> weightedLoadBalancer(
            Environment environment, LoadBalancerClientFactory factory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new WeightedRoundRobinLoadBalancer(
            factory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21