2. 负载均衡基础:概念、作用与常用算法

说起负载均衡,很多刚入行的朋友第一反应是“这不就是个反向代理嘛,配个轮询就完事了”。嗯,这么说也对,但只说对了一半。我自己的经验是,负载均衡是整个服务稳定性的底盘,选错了算法,后面加再多节点也没用。

说白了,负载均衡就是把你收到的请求,按照某种策略分发给后端的多个服务器。客户不会直接访问你的业务节点,而是先打到负载均衡器上,由它来决定“这活交给谁干”。

2.1 为什么要做负载均衡?

想想看,如果只有一个后端节点,它挂了怎么办?流量高峰来了压力全压它身上,响应慢了用户投诉,CPU 满了直接 OOM。你肯定不希望这样。

负载均衡解决的核心问题有三个:

  • 高可用 —— 一台挂了,流量切到其他节点,服务不中断。我在项目中见过因 MySQL 主库扛不住而切备机,但前端负载均衡没配好,备机没流量,用户全超时。血的教训。
  • 水平扩展 —— 加机器就能扛更多请求,不用改动业务代码。只要你把新节点注册到 LVS 或 Nginx 上游列表里就行。
  • 故障隔离 —— 负载均衡器配合健康检查,能自动摘掉有问题的节点。我曾经没配超时检测,一台服务器 Java 进程假死,负载均衡还在往它那发请求,结果部分用户永远在转圈。

2.2 负载均衡器的分类

你可能听过四层和七层的说法。我简单解释一下:

类型 工作层级 常见实例 适用场景
四层(L4) 传输层(IP+端口) LVS、F5、HAProxy(TCP模式) 高吞吐、低延迟,如数据库、游戏服务
七层(L7) 应用层(HTTP/HTTPS) Nginx、HAProxy(HTTP模式)、Traefik 需要根据域名、URL、Cookie 做分发,如 Web API
DNS 轮询 应用层(域名解析) DNS A 记录多值 全局 GSLB,简单但无法感知后端健康

咱们加密服务通常走七层,因为要终止 TLS、根据 SNI 域名做分发。四层适合纯 TCP 透传,比如数据库连接池。

2.3 常用负载均衡算法

算法是负载均衡的灵魂。我亲眼见过因为选了“轮询”导致 CPU 不均衡的案例——后端机器配置不一样,新的机器性能强却只分到和旧机器一样多的请求。下面我把常用算法列出来,你对比看看。

2.3.1 轮询(Round Robin)

最简单。请求按顺序轮流分配给后端。没有权重,一视同仁。

# Nginx 上游配置
upstream backend {
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
    server 10.0.0.3:8080;
}

特点:实现简单,开销小。但如果有慢请求积压,会引发“队头阻塞”。我建议只用在后端机器配置完全一致的场景。

2.3.2 加权轮询(Weighted Round Robin)

给每个节点配个 weight,比如新机器 weight=3,旧机器 weight=1,新机器拿到的请求数是旧机器的 3 倍。

upstream backend {
    server 10.0.0.1:8080 weight=3;
    server 10.0.0.2:8080 weight=1;
}

注意:加权轮询只是“概率均匀”,不保证严格比例。如果你的客户端长连接很多,用加权最小连接更合适。

2.3.3 最少连接(Least Connections)

负载均衡器记录每个节点当前的活跃连接数,每次把新请求发给连接数最少的那个。

upstream backend {
    least_conn;
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
}

适用场景:请求处理时间差异大时效果最好。比如有的请求查缓存快,有的查慢查询慢。我在做图片处理服务时就用的 least_conn,因为图片大小差很多,轮询会导致性能差的那台堆满连接。

2.3.4 IP 哈希(IP Hash)

根据客户端的 IP 地址计算哈希,把同一来源的请求始终发给同一个后端。

upstream backend {
    ip_hash;
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
}

为什么要用它? 最常见的场景是会话粘性——用户登录状态保存在本机 Session 里,如果下次请求跳到别的机器,Session 丢了,用户得重新登录。IP hash 能保证同一 IP 永远落在同一节点。不过要注意:如果客户端使用了 NAT 或 IPv6 前缀,hash 可能会不均。我在某电商平台就遇见过,公司出口 IP 只有几个,所有内部员工都挤到同一台机器上去了……

2.3.5 一致性哈希(Consistent Hashing)

这是 IP hash 的升级版。当后端节点增减时,只有少部分请求会被重新映射,不会像 IP hash 那样导致大量 Session 漂移。常用于缓存集群(比如 Redis、Memcached)的负载均衡。

# Nginx 通过 ngx_http_upstream_consistent_hash 模块
upstream backend {
    consistent_hash $request_uri;
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
    server 10.0.0.3:8080;
}

对比一下四种主要算法:

算法 平衡性 带权支持 会话保持 节点增减影响 典型场景
轮询 好(配置相同) 支持加权 否 无影响 无状态 API
最少连接 最好(请求时长不等) 支持加权 否 无影响 长连接、耗时差异大
IP 哈希 中等(取决于 IP 分布) 一般不支持 是 影响大 有状态会话
一致性哈希 较好 可扩展 是 影响小 缓存集群、分布式存储

2.4 如何选择算法?

没有银弹。我个人的做法是:先看业务状态。

  • 如果是加密服务(SSL 卸载、证书管理),请求基本无状态,轮询或最少连接都行。但要注意 TLS 握手有 CPU 消耗,最少连接能更均衡。
  • 如果后端需要保持会话(比如用户登录后的 Session),用 IP hash 加一层 Redis Session 共享更靠谱。我以前踩过坑——只用 IP hash,结果运维一扩缩容节点,哈希打乱,session 全跑调了。后来改了一致性哈希才稳住。
  • 后端机器配置不同时,加权轮询或加权最少连接是必须的,否则所有流量往弱鸡服务器上灌就等于自杀。
避坑提醒: 我经历过一次线上事故,用了 Nginx 的 ip_hash,结果客户端是某个运营商 NAT 出口,几千个用户共享一个公网 IP,全部堆积在同一台服务器上,CPU 直接 100%。如果你无法保证客户 IP 分布均匀,要么做一致性哈希,要么在应用层做分布式 Session。

2.5 负载均衡与健康检查的关系

光有均衡算法还不够。你想想看,一个节点已经挂了,负载均衡器还傻傻地往它那发请求,算法再好也没用。这时候就得靠健康检查来保命了。

负载均衡器周期性向后端节点发送探测请求(TCP 端口探活、HTTP 状态码检查、甚至自定义脚本),根据返回值判断节点状态。一旦发现节点异常,自动把它从上游列表中摘掉,不再分配新请求。等恢复正常后,再自动加回来。

健康检查的时机、间隔、超时、重试次数,每个参数都会影响系统稳定性。这部分我们下一章会细讲。

下面我用一张流程图把核心概念串起来 —— 从客户端请求到负载均衡器选择算法,再到健康检查的动态剔除与恢复。

负载均衡 + 健康检查流程 客户端请求 负载均衡器 LVS / Nginx / HAProxy 节点选择算法 轮询 / 最少连接 IP哈希 / 一致性哈希 后端节点 1 10.0.0.1:8080 后端节点 2 10.0.0.2:8080 节点 3 (失效) 已摘除 (不分配) 健康检查模块 TCP/HTTP 探活 反馈节点状态,更新上游列表 提示:健康检查会定时探测节点。 一旦发现节点 3 不可达,负载均衡器 立即把它从算法候选列表剔除。

从这张图你也能看出来,负载均衡器和健康检查是双胞胎,谁也离不开谁。算法决定“怎么分”,健康检查决定“分给谁”。下一章我们会深入健康检查的具体实现和参数调优,这里先留个印象。

2.6 小结

这一章我们聊了负载均衡的定义、为什么需要它、四层七层的区别,以及五种常用算法的原理和适用场景。我特别强调了一句:算法没有好坏,关键看你的后端服务有没有状态、机器配置是否一致、请求处理时间是否均匀。嗯,记住这两点,你在设计加密服务负载均衡方案时就能少走很多弯路。