4. 静态负载均衡算法:轮询、加权轮询、源地址哈希、随机算法的实现与优缺点

静态负载均衡算法,说白了就是“事先定好规则,运行时照着办”。它不关心后端服务器的实时负载、响应时间,也不管CPU是不是已经飙到90%。规则写死了,请求来了就按规则分配。

我刚开始做分布式系统时,觉得静态算法太“笨”了。后来在某个高并发项目里,动态算法因为频繁采集指标反而把监控系统打挂了……嗯,从那以后我明白了:简单,有时候就是最大的优势。

核心观点:静态算法适合后端服务能力均匀、请求处理成本相近的场景。它没有额外开销,性能极高,是很多网关和代理的默认选择。

静态负载均衡算法体系 静态负载均衡 轮询 (Round Robin) 加权轮询 (WRR) 源地址哈希 (IP Hash) 随机 (Random) 各算法核心特征 • 轮询:无状态,请求均匀分配,适合同配置服务器 • 加权轮询:按权重分配,适合异构服务器集群 • 源地址哈希:相同IP始终落在同一台服务器,支持会话保持 • 随机:概率上均匀,实现最简单,适合大流量场景

4.1 轮询算法 (Round Robin)

轮询是最基础的算法。请求按顺序依次分配给后端服务器,循环往复。就像打牌时轮流发牌,每人一张,公平得很。

// 轮询算法核心实现
public class RoundRobin {
    private List<String> servers;
    private int currentIndex = 0;
    
    public RoundRobin(List<String> servers) {
        this.servers = servers;
    }
    
    public String getNextServer() {
        // 原子操作,防止并发问题
        int index = currentIndex;
        currentIndex = (currentIndex + 1) % servers.size();
        return servers.get(index);
    }
}

我的经验:轮询算法在服务器配置完全一致时效果最好。我曾经在一个项目里用轮询,结果发现某台老机器总是先挂掉——后来一查,那台机器的内存只有其他机器的一半。轮询不会考虑这些差异,它只管“轮流”。

维度 说明
优点 实现简单,无状态,请求分配均匀
缺点 不考虑服务器性能差异,慢服务器容易成为瓶颈
适用场景 服务器配置相同、请求处理成本相近的集群

4.2 加权轮询算法 (Weighted Round Robin)

加权轮询解决了轮询的“一刀切”问题。给每台服务器设置一个权重,权重高的服务器分到更多请求。说白了,就是“能者多劳”。

// 加权轮询算法核心实现
public class WeightedRoundRobin {
    private List<Server> servers;
    private int currentIndex = 0;
    private int currentWeight = 0;
    
    static class Server {
        String ip;
        int weight;    // 权重,如 5、3、2
    }
    
    public String getNextServer() {
        while (true) {
            currentIndex = (currentIndex + 1) % servers.size();
            if (currentIndex == 0) {
                currentWeight--;
                if (currentWeight <= 0) {
                    currentWeight = getMaxWeight();
                }
            }
            if (servers.get(currentIndex).weight >= currentWeight) {
                return servers.get(currentIndex).ip;
            }
        }
    }
}

我曾经踩过的坑:权重设置不是线性的。权重5和权重3,不是说性能比是5:3,而是请求分配比例是5:3。如果服务器性能差异很大,建议先压测,再根据实际QPS来定权重。我见过有人拍脑袋设权重,结果高权重服务器被打爆了。

维度 说明
优点 支持异构服务器,分配比例可控
缺点 权重需要人工维护,无法动态调整
适用场景 服务器配置不同、需要按能力分配流量的集群

4.3 源地址哈希算法 (IP Hash)

源地址哈希,就是对客户端IP做哈希计算,然后取模得到服务器索引。同一个IP的请求,永远落在同一台服务器上。你想想看,这天然支持了会话保持,不需要额外的Session共享机制。

// 源地址哈希算法核心实现
public class IpHash {
    private List<String> servers;
    
    public IpHash(List<String> servers) {
        this.servers = servers;
    }
    
    public String getServer(String clientIp) {
        // 使用客户端的IP地址进行哈希
        int hashCode = clientIp.hashCode();
        // 取值,防止负数
        int index = Math.abs(hashCode) % servers.size();
        return servers.get(index);
    }
}

注意:哈希算法的均匀性很重要。我建议使用一致性哈希(Consistent Hash)来替代简单的取模哈希,这样当服务器扩缩容时,只有少量请求需要重新分配,不会引发“缓存雪崩”。

维度 说明
优点 天然会话保持,实现简单
缺点 服务器扩缩容会导致大量请求重新分配;可能造成负载不均
适用场景 需要会话粘滞、有状态服务的场景

4.4 随机算法 (Random)

随机算法,就是从服务器列表中随机选一台。听起来很不靠谱?其实在大流量下,随机算法的分布非常接近均匀。而且它实现最简单,连计数器都不需要。

// 随机算法核心实现
public class RandomLoadBalance {
    private List<String> servers;
    private Random random = new Random();
    
    public RandomLoadBalance(List<String> servers) {
        this.servers = servers;
    }
    
    public String getServer() {
        int index = random.nextInt(servers.size());
        return servers.get(index);
    }
}

我个人习惯:在压测或临时扩容时,我经常用随机算法。因为它不需要维护任何状态,扩缩容时只要更新服务器列表就行。但要注意,如果服务器数量很少(比如2台),随机算法可能短期出现明显不均,这时候轮询更靠谱。

维度 说明
优点 实现最简单,无状态,扩缩容影响小
缺点 小流量下分布不均,无法保证会话保持
适用场景 大流量、无状态服务、临时扩容场景

4.5 四种算法对比总结

说了这么多,到底怎么选?我整理了一张对比表,你一看就明白。

算法 实现复杂度 负载均匀性 会话保持 扩缩容影响 推荐场景
轮询 ★☆☆☆☆ ★★★★★ 不支持 小 同配置服务器集群
加权轮询 ★★☆☆☆ ★★★★☆ 不支持 小 异构服务器集群
源地址哈希 ★★☆☆☆ ★★★☆☆ 支持 大 有状态服务
随机 ★☆☆☆☆ ★★★★☆ 不支持 极小 大流量无状态服务

最后提醒一句:静态算法虽然简单高效,但有一个致命缺陷——它不感知后端服务器的健康状态。如果某台服务器挂了,静态算法依然会把请求发过去。所以实际生产环境中,静态算法一定要配合健康检查机制使用。我见过有人只用轮询,结果一台服务器宕机了,整个集群的失败率直接飙升到25%。