当前位置: 代码网 > it编程>编程语言>Java > 从零带你手写Java七种负载均衡算法实现方案

从零带你手写Java七种负载均衡算法实现方案

2026年02月13日 Java 我要评论
在分布式系统、微服务架构以及高并发场景中,负载均衡(load balancing) 是一项至关重要的技术。它能够将请求合理地分发到多个服务节点上,从而提升系统整体的吞吐量、可用性和容错能力。本文将带你

在分布式系统、微服务架构以及高并发场景中,负载均衡(load balancing) 是一项至关重要的技术。它能够将请求合理地分发到多个服务节点上,从而提升系统整体的吞吐量、可用性和容错能力。

本文将带你纯手撸实现七种常见的负载均衡算法,全部使用 java 编写,不依赖任何第三方框架,帮助你深入理解其核心原理与适用场景。

准备工作

首先定义一个通用的服务节点接口:

public class server {
    private string host;
    private int port;
    private int weight; // 权重,用于加权类算法

    public server(string host, int port) {
        this(host, port, 1);
    }

    public server(string ​​host, int port, int weight) {
        this.host = host;
        this.port = port;
        this.weight = weight;
    }

    // getters & setters
    public string gethost() { return host; }
    public int getport() { return port; }
    public int getweight() { return weight; }
    public void setweight(int weight) { this.weight = weight; }

    @override
    public string tostring() {
        return host + ":" + port + "(w=" + weight + ")";
    }
}

所有算法都将实现以下接口:

public interface loadbalancer {
    server select(list<server> servers);
}

1. 随机(random)

最简单的策略:从可用节点中随机选择一个。

public class randomloadbalancer implements loadbalancer {
    private final random random = new random();

    @override
    public server select(list<server> servers) {
        if (servers == null || servers.isempty()) return null;
        int index = random.nextint(servers.size());
        return servers.get(index);
    }
}

优点:简单、无状态

缺点:无法保证请求分布均匀(尤其在短时间窗口内)

2. 轮询(round robin)

按顺序依次选择节点,循环往复。

public class roundrobinloadbalancer implements loadbalancer {
    private atomicinteger index = new atomicinteger(0);

    @override
    public server select(list<server> servers) {
        if (servers == null || servers.isempty()) return null;
        int i = index.getandincrement() % servers.size();
        // 处理负数(虽然 unlikely)
        if (i < 0) i += servers.size();
        return servers.get(i);
    }
}

优点:请求分布均匀

缺点:未考虑服务器性能差异

3. 加权轮询(weighted round robin)

为每个节点分配权重,高权重节点被选中的频率更高。

实现思路:采用“最大公约数 + 当前轮次”方式,避免预生成列表(节省内存)。

public class weightedroundrobinloadbalancer implements loadbalancer {
    private atomicinteger currentpos = new atomicinteger(0);

    @override
    public server select(list<server> servers) {
        if (servers == null || servers.isempty()) return null;

        // 计算总权重
        int totalweight = servers.stream().maptoint(server::getweight).sum();
        if (totalweight <= 0) {
            // 退化为普通轮询
            return new roundrobinloadbalancer().select(servers);
        }

        int current = currentpos.getandincrement() % totalweight;
        if (current < 0) current += totalweight;

        // 遍历找到对应节点
        for (server server : servers) {
            if (current < server.getweight()) {
                return server;
            }
            current -= server.getweight();
        }

        // 理论上不会走到这里
        return servers.get(servers.size() - 1);
    }
}

注意:上述实现是简化版。工业级实现(如 nginx)通常使用更复杂的平滑加权轮询(smooth weighted round robin),以避免连续选中高权重节点。

4. 平滑加权轮询(smooth weighted round robin)

由 nginx 提出,解决加权轮询中“高权重节点连续被选中”的问题。

public class smoothweightedroundrobinloadbalancer implements loadbalancer {

    private final map<server, integer> currentweights = new concurrenthashmap<>();

    @override
    public server select(list<server> servers) {
        if (servers == null || servers.isempty()) return null;

        int totalweight = 0;
        server best = null;
        int max = integer.min_value;

        for (server server : servers) {
            int weight = server.getweight();
            if (weight <= 0) weight = 1;

            totalweight += weight;
            int current = currentweights.getordefault(server, 0) + weight;
            currentweights.put(server, current);

            if (current > max) {
                max = current;
                best = server;
            }
        }

        if (best != null) {
            currentweights.put(best, max - totalweight);
        }

        return best;
    }
}

优点:权重分配更平滑,高权重节点不会连续被选中

示例:a(w=5), b(w=1) → 顺序为 a,a,a,a,a,b,... 而非 a,a,a,a,a,a,...

5. 最少连接(least connections)

将请求分发给当前连接数最少的节点。

为简化,我们用一个 map<server, atomicinteger> 模拟连接计数。

public class leastconnectionsloadbalancer implements loadbalancer {
    private final map<server, atomicinteger> connectioncounts = new concurrenthashmap<>();

    @override
    public server select(list<server> servers) {
        if (servers == null || servers.isempty()) return null;

        server best = null;
        int minconn = integer.max_value;

        for (server server : servers) {
            int conn = connectioncounts.computeifabsent(server, k -> new atomicinteger(0)).get();
            if (conn < minconn) {
                minconn = conn;
                best = server;
            }
        }

        // 模拟增加连接(实际使用需配合请求完成后的 decrement)
        if (best != null) {
            connectioncounts.get(best).incrementandget();
        }

        return best;
    }

    // 供外部调用:请求完成后减少连接数
    public void release(server server) {
        atomicinteger count = connectioncounts.get(server);
        if (count != null) {
            count.decrementandget();
        }
    }
}

适用于长连接或处理时间差异大的场景

需要维护连接状态,有额外开销

6. 源地址哈希(ip hash / source hash)

根据客户端 ip(或其他唯一标识)做哈希,保证同一客户端始终路由到同一节点。

public class sourcehashloadbalancer implements loadbalancer {
    private string source; // 可通过构造函数传入 client ip

    public sourcehashloadbalancer(string source) {
        this.source = source;
    }

    @override
    public server select(list<server> servers) {
        if (servers == null || servers.isempty() || source == null) return null;
        int hash = source.hashcode();
        int index = (hash & 0x7fffffff) % servers.size(); // 避免负数
        return servers.get(index);
    }
}

优点:会话保持(session stickiness)

缺点:节点增减会导致大量映射失效(可改用一致性哈希)

7. 一致性哈希(consistent hashing)

解决普通哈希在节点动态变化时缓存/会话大量失效的问题。

public class consistenthashingloadbalancer implements loadbalancer {
    private final sortedmap<integer, server> circle = new treemap<>();
    private final int virtualnodes; // 虚拟节点数

    public consistenthashingloadbalancer(int virtualnodes) {
        this.virtualnodes = virtualnodes;
    }

    public void addserver(server server) {
        for (int i = 0; i < virtualnodes; i++) {
            int hash = hash(server.gethost() + ":" + server.getport() + "#" + i);
            circle.put(hash, server);
        }
    }

    public void removeserver(server server) {
        for (int i = 0; i < virtualnodes; i++) {
            int hash = hash(server.gethost() + ":" + server.getport() + "#" + i);
            circle.remove(hash);
        }
    }

    private int hash(string key) {
        return key.hashcode(); // 简化,生产建议用 md5 或 murmurhash
    }

    @override
    public server select(list<server> servers) {
        if (circle.isempty()) {
            // 动态构建环(实际应提前构建)
            servers.foreach(this::addserver);
        }
        if (circle.isempty()) return null;

        // 假设 source 为请求 id 或 ip
        string requestkey = "request_" + system.nanotime(); // 实际应由调用方提供
        int hash = hash(requestkey);

        if (!circle.containskey(hash)) {
            sortedmap<integer, server> tailmap = circle.tailmap(hash);
            hash = tailmap.isempty() ? circle.firstkey() : tailmap.firstkey();
        }
        return circle.get(hash);
    }
}

节点增减只影响局部数据

广泛用于缓存、分布式存储系统

总结对比

算法是否考虑权重是否有状态适用场景
随机简单快速分发
轮询✅(位置)请求均匀、节点同质
加权轮询节点性能不同
平滑加权轮询更公平的加权分发
最少连接❌(但看负载)长连接、异构任务
源地址哈希会话保持
一致性哈希❌(可扩展支持)✅(哈希环)缓存、分布式存储

结语

通过手写这七种负载均衡算法,我们不仅掌握了其实现细节,也理解了它们各自的优劣和适用边界。在真实项目中,可根据业务需求灵活选择或组合使用(例如:先一致性哈希定位节点组,再在组内轮询)。

提示:生产环境建议使用成熟组件(如 ribbon、spring cloud loadbalancer、nginx、lvs),但理解底层原理永远是工程师的核心竞争力。

以上就是从零带你手写java七种负载均衡算法实现方案的详细内容,更多关于java负载均衡算法的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com