负载均衡器(load balancer)位于客户端和一组后端服务器之间,分发入站流量,让任何单台服务器都不会成为瓶颈。它是横向扩展的基石:加更多服务器,负载均衡器就自动分摊工作。没有它,横向扩展在运维上不可能,且单台服务器故障会拖垮整个服务。

⚡ 速览要点
  • L4 vs L7——L4 按 TCP/UDP 头路由(快、不检查内容);L7 按 HTTP 路径/头/cookie 路由(更聪明、CPU 略高)。
  • 轮询(Round Robin)适合均匀负载;最少连接(Least Connections)适应时长可变的请求。
  • IP 哈希无需存状态就给出会话亲和性,但移除一台服务器时会重排所有客户端。
  • 粘性会话是有状态服务器的权宜之计——真正的修法是把会话状态外置到 Redis。
  • LB 冗余——用带 VIP 的 active-passive 或 active-active 对;LB 本身绝不能是 SPOF。
tldr

负载均衡器用轮询、最少连接或 IP 哈希等算法把流量路由到后端服务器。L4 LB 按 TCP/UDP 元数据路由;L7 LB 检查 HTTP 内容、能做更聪明的路由决策。健康检查自动把不健康的服务器移出池。

负载均衡把流量分发到后端服务器
负载均衡把流量分发到后端服务器

为什么要负载均衡?

它解决三个核心问题:

第四层 vs. 第七层

维度第四层(传输层)第七层(应用层)
作用于TCP/UDP 头(IP + 端口)HTTP 头、URL、cookie、body
性能非常快——不检查内容CPU 成本略高
路由智能低——不感知应用协议高——能按路径、host 或头值路由
TLS 终止仅透传可终止 TLS、检查载荷、再加密
示例AWS NLB、HAProxy TCP 模式AWS ALB、Nginx、Envoy

实践中,大多数现代架构在边缘用 L7 负载均衡器(做 HTTP 路由、TLS 终止、头操作),对内部 TCP 服务用 L4 负载均衡器——那里裸吞吐比路由智能更重要。

负载均衡算法

轮询(Round Robin)

请求按循环顺序依次分发到服务器池。当所有服务器容量相同、请求成本均匀时简单有效。加权轮询(Weighted Round Robin)扩展它:给每台服务器分配与其容量成比例的权重,权重 3 的服务器接收的请求是权重 1 的三倍。

最少连接(Least Connections)

每个新请求发给当前处理活跃连接最少的服务器。它自然适应请求时长可变的负载——处理长查询的服务器持有更少的新连接。加权最少连接在比较前先把活跃连接数除以服务器权重。

IP 哈希(IP Hash)

对客户端 IP 地址做哈希以确定性地选服务器。同一客户端总落到同一后端,提供会话亲和性而无需负载均衡器存会话状态。缺点:若移除一台服务器,它的所有客户端都被重新哈希和重分配,可能打断进行中的会话。

随机(Random)

均匀随机选一台服务器。在大请求量下统计上接近轮询,但小请求量无保证。当你想要零协调开销、且池大而同质时有用。

健康检查

负载均衡器定期探测后端以检测故障。一台服务器在连续失败可配置的次数后被标记为不健康并移出池;在连续通过可配置的次数后被重新加入。有两种口味:

nginx
upstream backend {
    least_conn;                          # 最少连接算法
    server 10.0.0.1:8080 weight=3;
    server 10.0.0.2:8080 weight=1;
    server 10.0.0.3:8080 backup;         # 仅在主服务器全挂时使用
}

server {
    listen 80;
    location / {
        proxy_pass http://backend;
        proxy_next_upstream error timeout http_500;
    }
}

粘性会话(Sticky Sessions)

有些应用把会话状态存在服务器上(进程内缓存、进行中的文件上传)。粘性会话(也叫会话保持)确保某客户端的请求总去同一后端。负载均衡器通常通过在客户端首次请求时设 cookie、在后续请求读它来实现。

粘性会话是双刃剑:它改善与有状态应用的兼容性,但降低负载的均匀分布并使故障处理复杂化——若粘住的服务器宕机,它的所有会话反正都丢了。首选修法是把会话状态移到共享存储(Redis、数据库),让任何服务器都能处理任何请求。

负载均衡器冗余

负载均衡器本身是潜在的单点故障。生产部署用 active-passiveactive-active 对,常配一个在节点间浮动的虚拟 IP(VIP)。DNS 或 BGP 把 VIP 路由到活跃节点;若它故障,被动节点通过 VRRP(HAProxy + Keepalived)等协议或更新 DNS 记录在数秒内接管 VIP。

一致性哈希(Consistent Hashing)

IP 哈希和简单的取模路由有一个灾难性缺陷:增删一台服务器几乎会重排所有客户端分配。若你有 10 台服务器加 1 台,模 11 对大多数 key 产生完全不同的目标——突然 90% 的缓存 key 在新服务器映射上未命中,在后端造成 thundering herd。一致性哈希通过把服务器和请求都映射到一个环形哈希环来解决:增删一台服务器只影响它与最近邻之间范围内的 key——对 n 台服务器大约 1/n 的 key。

环怎么工作

每台服务器被哈希到 0–2³²−1 环上的一个或多个点。每个请求 key 也被哈希到环上一个点;请求路由到从该点顺时针走遇到的第一台服务器。用虚拟节点(vnode),每台物理服务器占环上多个点(如每台 150 个 vnode),平滑掉服务器哈希聚集在环某一区域时的不均匀分布。

python
import hashlib, bisect

class ConsistentHashRing:
    def __init__(self, nodes, vnodes=150):
        self.ring = {}        # hash → node
        self.keys = []        # 排序的哈希值
        for node in nodes:
            for i in range(vnodes):
                h = self._hash(f"{node}-{i}")
                self.ring[h] = node
                self.keys.append(h)
        self.keys.sort()

    def _hash(self, key):
        return int(hashlib.md5(key.encode()).hexdigest(), 16)

    def get_node(self, key):
        h = self._hash(key)
        idx = bisect.bisect(self.keys, h) % len(self.keys)
        return self.ring[self.keys[idx]]   # 顺时针后继

一致性哈希被用于:分布式缓存(Memcached、Redis Cluster)、CDN 边缘服务器、数据库分片路由器,以及 Kafka 的分区分配。每当会话亲和性或缓存局部性重要、且你需要增删节点而不造成灾难性 key 重映射时,它就是答案。

L4 vs L7——更深入

高层区别众所周知,但实现细节决定每种场景该选哪个。

第四层的底层

L4 负载均衡器在 TCP/UDP 传输层工作。它看到源 IP、目的 IP、源端口、目的端口——别的什么都看不到。两种主流转发策略是:

第七层的底层

L7 负载均衡器终止来自客户端的 TCP 连接,读取 HTTP/gRPC/WebSocket 请求,基于内容做路由决策,再与后端建立一条独立的 TCP 连接。这种双连接模型使得 L4 无法提供的特性成为可能:

gRPC 与 HTTP/2

gRPC 在单条长连接 HTTP/2 上多路复用许多调用。L4 负载均衡器把整条连接路由到一个后端——该连接上的所有调用都落到同一台服务器,彻底破坏了负载均衡。修法是用理解 HTTP/2 的 L7 代理(Envoy、带 gRPC 代理模块的 Nginx、Linkerd)把单个 gRPC 帧分摊到后端。

SSL/TLS 终止策略

负载均衡架构中处理 TLS 有三种标准方式,各有不同的安全/性能取舍:

策略描述优点缺点
在 LB 终止 TLSLB 解密流量;后端收到明文 HTTP集中证书管理;LB 能按内容检查/路由;后端更简单内部流量不加密;LB 成了解密 oracle
TLS 透传(L4)LB 转发加密字节;后端终止 TLS端到端加密;LB 从不见明文无法做 L7 路由;每后端管理证书
TLS 重加密LB 终止来自客户端的 TLS,再开一条新 TLS 连接到后端两段都加密;LB 能检查和路由CPU 成本更高;要管两对证书

大多数云架构用边缘 LB 的 TLS 终止(AWS ALB、Cloudflare)结合内部 mTLS(服务间的双向 TLS,由 Istio 或 Linkerd 等服务网格强制)。这给你边缘的 L7 路由、集中的公网证书管理,以及端到端加密,而无需手工管理后端证书。

nginx
# TLS 终止 + 重加密到后端
server {
    listen 443 ssl;
    ssl_certificate     /etc/ssl/certs/api.example.com.crt;
    ssl_certificate_key /etc/ssl/private/api.example.com.key;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;

    location / {
        proxy_pass              https://backend;  # 重加密到后端
        proxy_ssl_verify        on;
        proxy_ssl_trusted_certificate /etc/ssl/certs/internal-ca.crt;
        proxy_ssl_session_reuse on;              # 复用 TLS 会话以提性能
    }
}

健康检查——高级配置

基础健康检查发一个 TCP SYN 或对 /health 的 HTTP GET。生产中,"进程还活着"和"进程能服务生产流量"之间的间隙正是大多数故障藏身之处。高级健康检查设计弥合这个间隙。

健康检查端点设计

设计良好的 /health 端点应检查所有关键依赖——数据库连通性、缓存可用性、下游服务可达性——并返回带每依赖状态的结构化 JSON。这让负载均衡器能区分"服务器彻底死了"(TCP 失败)和"服务器活着但数据库挂了"(HTTP 503),据此路由。

json
// GET /health/readiness — 就绪返回 200 OK,否则 503
{
  "status": "degraded",
  "checks": {
    "database":  { "status": "up",   "latency_ms": 3   },
    "cache":     { "status": "up",   "latency_ms": 0.5 },
    "payment_gw":{ "status": "down", "latency_ms": 5000 }
  }
}

HAProxy 健康检查配置

haproxy
backend api_servers
    balance leastconn
    option httpchk GET /health/readiness
    http-check expect status 200
    default-server inter 5s fall 3 rise 2   # 每 5s 检查;3 次失败=down,2 次成功=up
    server api1 10.0.0.1:8080 check weight 10
    server api2 10.0.0.2:8080 check weight 10
    server api3 10.0.0.3:8080 check weight 5   # 小实例——权重减半
    server api4 10.0.0.4:8080 check backup      # 仅在所有主服务器全挂时激活

DNS 负载均衡与全局流量管理

基于 DNS 的负载均衡完全在网络层之上工作:DNS 服务器给不同客户端返回不同的 A 记录,把它们分散到服务器池或地理区域。它是全局流量管理和多区域部署的基础。

DNS LB 怎么工作

你域名的权威 DNS 服务器根据策略返回不同 IP 地址——跨 IP 的简单轮询,或基于解析器 IP 的地理路由。客户端在 TTL 期间(常 30–300 秒)缓存响应,然后重新解析。这在 DNS 层、任何 TCP 连接建立之前,创造了天然的负载分布。

dns
# 简单 DNS 轮询:连续查询返回不同 IP
api.example.com.  30  IN  A  54.1.2.3    # US-East LB VIP
api.example.com.  30  IN  A  52.4.5.6    # US-West LB VIP
api.example.com.  30  IN  A  13.7.8.9    # EU-West LB VIP

# DNS TTL = 30s:客户端缓存 30s,然后重新解析到不同 IP
# 低 TTL 实现快速故障转移,但增加 DNS 查询负载

DNS LB 的局限

GeoDNS 与基于延迟的路由

AWS Route 53、Cloudflare、Akamai 等服务给 DNS 加上地理智能:它们识别解析器的位置(通常代理用户位置)并返回最近的健康端点 IP。欧洲用户把 api.example.com 解析到法兰克福 IP;新加坡用户解析到新加坡 IP。结合把不健康端点移出轮转的健康检查,GeoDNS 除了延迟优化还提供区域级故障转移。

Anycast 与全局负载均衡

Anycast 是一种路由技术:同一个 IP 地址通过 BGP 从多个地理位置同时通告。互联网的路由协议自然把包送到拓扑上最近的源——来自欧洲的流量到达欧洲 PoP,来自亚洲的到达亚洲 PoP,全都指向同一目的 IP。这就是 CDN 和 DDoS 清洗服务的工作方式:一个 IP 地址出现在数百个边缘位置。

Anycast 与 DNS LB 有一个重要区别:路由决策在网络层(BGP)做,不在应用层。没有 TTL 缓存问题——包在每一跳被路由到最近 PoP。而且因为 PoP 故障时 BGP 在数秒内重新收敛,故障转移比等 DNS TTL 过期更快更可靠。

cloudflare 示例

Cloudflare 从全球 300+ 数据中心通告同一个 IP 地址(如其 DNS 解析器的 1.1.1.1,或其 CDN 的 anycast 网络)。东京用户连到东京边缘节点;圣保罗用户连到圣保罗边缘节点——全都指向同一 IP。这就是规模化的 Anycast:用单个 IP 地址实现全局负载分布和容错。

会话保持深入

粘性会话有几种实现变体,取舍差异显著。理解它们对生产运维和面试中讨论有状态应用都重要。

基于 Cookie 的亲和性

负载均衡器在客户端首次请求时设一个 cookie(如 SERVERID=api2)。后续请求,LB 读 cookie 并路由到指定后端。这是最常见且最灵活的方式——它跨 IP 变化也能工作(移动客户端切网络)、不暴露后端 IP,且 cookie TTL 控制亲和性持续多久。

haproxy
backend web_servers
    balance roundrobin
    cookie SERVERID insert indirect nocache   # LB 管理的粘性 cookie
    server web1 10.0.0.1:80 check cookie web1
    server web2 10.0.0.2:80 check cookie web2
    server web3 10.0.0.3:80 check cookie web3

IP 哈希亲和性

对客户端 IP 哈希以确定性选后端。无 cookie 开销,但在网络间漫游的移动客户端会被重分配,而企业客户端(许多用户在一个 NAT IP 后)全落到同一后端。增删服务器会改变与 1/n 成比例的客户端的哈希结果(假设一致性哈希)或几乎所有客户端(朴素取模)。

正确修法:外置会话状态

两种方式都是对一个更深层问题的权宜之计:会话状态存在应用服务器内存里。生产级解法是把会话状态移到共享、快速的存储如 Redis 或 Memcached。任何后端都能处理任何请求,因为会话从外部存储查,而非本地内存。这实现了真正弹性的横向扩展:增删服务器而无任何客户端可见的中断。

高可用与故障转移模式

本身有单点故障的负载均衡器解决了错误的问题。生产 LB 配置用两种高可用模式之一。

带 VIP 的 Active-Passive

两个负载均衡器节点共享一个虚拟 IP(VIP)。正常运行时,只有活跃节点占据 VIP 并处理所有流量。被动节点通过心跳(VRRP、keepalived)监视活跃节点。若活跃节点在可配置窗口内未发心跳,被动节点通过 gratuitous ARP 占据 VIP 并接管。故障转移通常在 1–2 秒内完成。AWS Elastic Load Balancer、Google Cloud LB、Azure Load Balancer 在内部管理这套——你从不见 VIP 机制。

bash
# keepalived.conf(VRRP active-passive LB 对)
vrrp_instance VI_1 {
    state MASTER            # 被动节点设为 BACKUP
    interface eth0
    virtual_router_id 51
    priority 110           # 优先级越高越优先做 master;BACKUP = 100
    advert_int 1           # VRRP 心跳每 1s
    authentication {
        auth_type PASS
        auth_pass secret123
    }
    virtual_ipaddress {
        203.0.113.10        # 两节点竞争的 VIP
    }
    track_script {
        chk_haproxy         # haproxy 进程死掉则降级
    }
}

Active-Active

两个 LB 节点同时处理流量,通常通过两个 VIP 间的 DNS 轮询或上游 Anycast 地址。总吞吐翻倍。故障时,存活节点吸收所有流量。取舍:配置和状态同步稍复杂(若要 TCP 会话无缝挺过故障转移,连接表必须复制)。

算法对比与选择指南

算法最适合弱点复杂度
轮询请求成本均匀、服务器相同请求时长不同或服务器容量不同则不均O(1)
加权轮询异构服务器容量仍忽略当前负载;静态权重需手动调O(1)
最少连接请求时长可变(long-polling、流式)所有新连接同时发给一台空闲服务器(恢复时 thundering herd)O(log n)
IP 哈希无 cookie 开销的会话亲和性增删服务器时重排;NAT 客户端造成热点O(1)
一致性哈希规模化的会话亲和性 + 缓存局部性无 vnode 时分布略不均;实现更复杂O(log n)
随机无状态请求的超大同质池小池无保证;不感知服务器负载O(1)
最短响应时间延迟敏感 API;按实际响应速度加权需要主动测量开销;可能放大 thundering herdO(log n)

现代架构中的负载均衡

服务网格(Service Mesh)

在微服务架构中,服务网格(Istio、Linkerd、Consul Connect)把负载均衡逻辑从中心 LB 设备移到一个与每个应用 Pod 并行运行的 sidecar 代理(通常是 Envoy)。每个 sidecar 拦截所有入站和出站流量,应用负载均衡、重试、熔断和 mTLS——应用代码毫不知情。控制平面(Istio 的 istiod、Linkerd 的控制平面)把配置更新分发给所有 sidecar。

优势:在每个服务间调用都做带每请求路由决策的 L7 负载均衡,而非只在边缘。代价:每个 Pod 现在有一个消耗约 50–100 MB RAM、每跳加约 1–2 ms 延迟的 sidecar。

AWS Application Load Balancer——关键特性

常见坑与最佳实践

总结

需要路径路由、TLS 终止或头检查的 HTTP 负载选第七层。需要裸 TCP 吞吐、非 HTTP 协议或通过直接服务器返回获极致性能选第四层。按请求同质性选算法:均匀负载用轮询,可变负载用最少连接,缓存局部性和会话亲和性用一致性哈希。始终至少跑两个 active-passive 或 active-active 配置的负载均衡器,以免引入你本要消除的那个 SPOF。

🎯 面试速答

L4 vs L7——何时选哪个?L4 用于裸 TCP 吞吐、非 HTTP 协议,或需要直接服务器返回让返回流量绕开 LB 时;L7 用于需要基于路径的路由、TLS 终止、gRPC 支持或基于头的决策时。
负载均衡器怎么检测故障服务器?主动健康检查(按计划对 /health 的合成探测)或被动健康检查(从真实流量的 5xx 错误率推断)。主动检查检测流量前故障;被动检查检测进行中故障。
粘性会话为什么是问题?它们降低负载分布、使扩展复杂化,并意味着该服务器宕机时所有会话数据都丢——修法是把会话状态外置到 Redis,让任何服务器处理任何请求。
一致性哈希是什么、何时用?把服务器和请求映射到环;增删服务器只影响约 1/n 的 key,而非重排一切。用于分布式缓存、CDN、数据库分片和 Kafka 分区分配。
Anycast 怎么工作?同一 IP 通过 BGP 从多个地理 PoP 通告;互联网路由自然把包送到拓扑最近的 PoP——无 DNS 解析、无 TTL 缓存、PoP 宕机时亚秒级故障转移。
L4 LB 为什么不能均衡 gRPC 流量?gRPC 在单条 HTTP/2 连接上多路复用许多调用;L4 LB 把整条连接路由到一个后端。只有理解 HTTP/2 帧的 L7 代理才能把单个调用分摊到后端。

← 上一篇
Kubernetes (k8s)