Skip to content

可以用几行代码实现一个负载均衡器吗? ​

  • 标签:后端、场景题

🏛 业务架构师 ​

可以,一个最简负载均衡器几行就够——但我会答出层次和边界,这也是这道低难度题的加分方式。

最简版:随机

java
List<String> nodes = List.of("10.0.0.1", "10.0.0.2", "10.0.0.3");
String node = nodes.get(ThreadLocalRandom.current().nextInt(nodes.size()));

升级一步:加权轮询(贴近生产)

java
// 权重 5:3:2, 平滑加权轮询
int total = nodes.stream().mapToInt(n -> n.weight).sum();
if (++seq >= total) seq = 0;
// 按累计权重区间选节点

我会主动补全的"真负载均衡器"要素(说明理解边界):

  1. 健康检查:节点摘除与恢复(挂了还往它发流量就是故障放大器)
  2. 策略选型:
    • 轮询/加权:无状态服务
    • 一致性哈希:有会话粘性需求(缓存命中、长连接路由)
    • 最少连接:请求耗时差异大的场景
  3. 失败重试与快速切换:转发失败换节点重试一次,对上游透明

业务架构视角收尾:这段代码适合回答"理解原理",但生产负载均衡的投入产出账是另一回事——用 Nginx/Dubbo 内置 LB 或 Spring Cloud LoadBalancer 就好,自研均衡器要处理的服务器探活、慢启动、权重热调等工程细节,价值全部在细节里。几行代码的版本帮我理解"均衡策略只是均衡器 10% 的部分",另外 90% 是可用性工程——这个认知比代码本身重要。

🏗 系统架构师 ​

可以,负载均衡器的最小内核就是一个并发安全的服务列表 + 选择策略,几十行就能写一个可用的客户端 LB。

java
public class SimpleLoadBalancer {
    private final List<String> servers = new CopyOnWriteArrayList<>();
    private final AtomicInteger idx = new AtomicInteger(0);

    public void add(String s)  { servers.add(s); }
    public void remove(String s) { servers.remove(s); }

    public String choose() {                    // 轮询
        List<String> list = servers;
        if (list.isEmpty()) return null;
        int i = Math.abs(idx.getAndIncrement());
        return list.get(i % list.size());
    }
}

这段代码里每个选型都有讲究:

  • CopyOnWriteArrayList:读多写少(节点变更低频),保证读不加锁且迭代安全
  • AtomicInteger 取模做轮询:CAS 无锁,避免 synchronized 争用;随机策略则直接 ThreadLocalRandom.nextInt(n),更简单
  • 真实调用处包一层 try-catch + 失败重选下一个节点,就有了故障转移雏形

但要说明它与生产级 LB 的差距,这体现架构视野:

  1. 健康检查:节点列表要由心跳/注册中心驱动(这就是 Ribbon/Spring Cloud LoadBalancer 与服务发现的结合点)
  2. 策略丰富度:加权轮询(权重=机器配置)、最少并发(ActiveCount 最小者,对慢节点自愈最好)、一致性哈希(会话粘性)、响应时间加权
  3. 软负载 vs 硬负载:客户端 LB(无单点、少一跳)与服务端 LB(Nginx/LVS,统一管控)的取舍;云原生下还有 service mesh sidecar 模式
  4. 平滑性:普通轮询在节点增删瞬间会抖动,加权要实现平滑轮询(Nginx 的 smooth weighted 算法)

答题节奏:先爽快写出代码,再用差距分析展示纵深。

💻 全栈开发专家 ​

可以,负载均衡器核心就一句话:从实例列表里按策略挑一个。最小实现几行:

轮询版

java
public class RoundRobin {
    private final List<String> servers = List.of("10.0.0.1", "10.0.0.2", "10.0.0.3");
    private final AtomicInteger idx = new AtomicInteger();

    public String next() {
        int i = Math.abs(idx.getAndIncrement() % servers.size());
        return servers.get(i);
    }
}

随机版(更简单,大数下均衡)

java
private final Random r = new Random();
public String next() { return servers.get(r.nextInt(servers.size())); }

加权轮询(贴合真实场景)

java
// 权重 5,1,1 -> 平滑加权:每次选 currentWeight 最大者,再各减总权重
public String next() {
    int total = servers.stream().mapToInt(s -> s.weight).sum();
    Server best = null;
    for (Server s : servers) {
        s.current += s.weight;
        if (best == null || s.current > best.current) best = s;
    }
    best.current -= total;
    return best.host;
}

面试加分点:真实负载均衡器(如 Ribbon/LoadBalancer)还要考虑——实例动态上下线(对接注册中心)、健康检查摘除故障节点、一致性哈希(有状态路由)、重试时换节点。上面代码是骨架,生产不重复造轮子。