接入 Nginx: 六种负载均衡策略原理与选型
纲要
Nginx 提供的负载均衡策略,把六种策略逐个拆开:
- 轮询(默认):严格交替,无状态
weight权重:按比例分发,适配异构机器ip_hash:按客户端IP哈希,实现会话保持least_conn:按最少连接数,适配长耗时请求url_hash:按请求URL哈希,提升缓存命中率fair:按响应时间,需第三方模块
#mermaid-svg-Ormz5cNuoAJeMOq9{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Ormz5cNuoAJeMOq9 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Ormz5cNuoAJeMOq9 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Ormz5cNuoAJeMOq9 .error-icon{fill:#552222;}#mermaid-svg-Ormz5cNuoAJeMOq9 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Ormz5cNuoAJeMOq9 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Ormz5cNuoAJeMOq9 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Ormz5cNuoAJeMOq9 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Ormz5cNuoAJeMOq9 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Ormz5cNuoAJeMOq9 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Ormz5cNuoAJeMOq9 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Ormz5cNuoAJeMOq9 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Ormz5cNuoAJeMOq9 .marker.cross{stroke:#333333;}#mermaid-svg-Ormz5cNuoAJeMOq9 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Ormz5cNuoAJeMOq9 p{margin:0;}#mermaid-svg-Ormz5cNuoAJeMOq9 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Ormz5cNuoAJeMOq9 .cluster-label text{fill:#333;}#mermaid-svg-Ormz5cNuoAJeMOq9 .cluster-label span{color:#333;}#mermaid-svg-Ormz5cNuoAJeMOq9 .cluster-label span p{background-color:transparent;}#mermaid-svg-Ormz5cNuoAJeMOq9 .label text,#mermaid-svg-Ormz5cNuoAJeMOq9 span{fill:#333;color:#333;}#mermaid-svg-Ormz5cNuoAJeMOq9 .node rect,#mermaid-svg-Ormz5cNuoAJeMOq9 .node circle,#mermaid-svg-Ormz5cNuoAJeMOq9 .node ellipse,#mermaid-svg-Ormz5cNuoAJeMOq9 .node polygon,#mermaid-svg-Ormz5cNuoAJeMOq9 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Ormz5cNuoAJeMOq9 .rough-node .label text,#mermaid-svg-Ormz5cNuoAJeMOq9 .node .label text,#mermaid-svg-Ormz5cNuoAJeMOq9 .image-shape .label,#mermaid-svg-Ormz5cNuoAJeMOq9 .icon-shape .label{text-anchor:middle;}#mermaid-svg-Ormz5cNuoAJeMOq9 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Ormz5cNuoAJeMOq9 .rough-node .label,#mermaid-svg-Ormz5cNuoAJeMOq9 .node .label,#mermaid-svg-Ormz5cNuoAJeMOq9 .image-shape .label,#mermaid-svg-Ormz5cNuoAJeMOq9 .icon-shape .label{text-align:center;}#mermaid-svg-Ormz5cNuoAJeMOq9 .node.clickable{cursor:pointer;}#mermaid-svg-Ormz5cNuoAJeMOq9 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Ormz5cNuoAJeMOq9 .arrowheadPath{fill:#333333;}#mermaid-svg-Ormz5cNuoAJeMOq9 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Ormz5cNuoAJeMOq9 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Ormz5cNuoAJeMOq9 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ormz5cNuoAJeMOq9 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Ormz5cNuoAJeMOq9 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ormz5cNuoAJeMOq9 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Ormz5cNuoAJeMOq9 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Ormz5cNuoAJeMOq9 .cluster text{fill:#333;}#mermaid-svg-Ormz5cNuoAJeMOq9 .cluster span{color:#333;}#mermaid-svg-Ormz5cNuoAJeMOq9 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Ormz5cNuoAJeMOq9 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Ormz5cNuoAJeMOq9 rect.text{fill:none;stroke-width:0;}#mermaid-svg-Ormz5cNuoAJeMOq9 .icon-shape,#mermaid-svg-Ormz5cNuoAJeMOq9 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ormz5cNuoAJeMOq9 .icon-shape p,#mermaid-svg-Ormz5cNuoAJeMOq9 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Ormz5cNuoAJeMOq9 .icon-shape .label rect,#mermaid-svg-Ormz5cNuoAJeMOq9 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ormz5cNuoAJeMOq9 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Ormz5cNuoAJeMOq9 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Ormz5cNuoAJeMOq9 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
是
能
不能
否
是
否
是
否
选择负载均衡策略
后端机器性能是否一致?
weight 权重
是否需要会话保持?
能否外置 Session?
Redis 共享 Session
- 任意策略
ip_hash
请求耗时是否差异大?
least_conn / fair
是否需要缓存亲和?
url_hash
轮询(默认)
一、策略总览
| 策略 | 配置写法 | 特点 | 是否内置 |
|---|---|---|---|
| 轮询 | 默认,不写 | 依次分发,严格交替 | ✓ |
weight |
server ... weight=10; |
权重越大概率越高 | ✓ |
ip_hash |
ip_hash; |
同一 IP 固定到同一台 |
✓ |
least_conn |
least_conn; |
优先给连接数最少的 | ✓ |
url_hash |
hash $request_uri; |
同一 URL 固定到同一台 |
✓(hash 指令) |
fair |
fair; |
优先给响应最快的 | ✗ 第三方模块 |
二、轮询(Round Robin)
配置
不需要任何配置,写了 upstream 就是轮询:
nginx
upstream targetserver {
server 192.168.138.101:8080;
server 192.168.138.101:8081;
}
行为
请求按顺序依次分发:
txt
请求 1 → 8080
请求 2 → 8081
请求 3 → 8080
请求 4 → 8081
...
第 n 个请求落到第 n % N 台(N 为节点数)。
适用与局限
适用:所有后端机器性能一致、请求处理耗时接近的场景。这是最常见的情况,所以轮询作为默认值是合理的。
局限:它假设"每个请求的代价相同"。如果某些请求是耗时 5 秒的报表导出,某些是 10 毫秒的简单查询,轮询会导致某台机器恰好堆积了多个慢请求,而其他机器空闲。
与 weight 的关系
轮询其实是 weight 都等于 1 的特例。一旦给某个节点配了 weight,实际使用的就是**加权轮询(WRR)**算法。
三、权重(weight)
配置
nginx
upstream targetserver {
server 192.168.138.101:8080 weight=10;
server 192.168.138.101:8081 weight=5;
}
行为
weight 默认值是 1。上面配置中 10 : 5 = 2 : 1,即 8080 收到的请求数约为 8081 的两倍。
实测刷新:
txt
第 1 次:8080
第 2 次:8081
第 3 次:8080
第 4 次:8080
第 5 次:8081
第 6 次:8080
大致是 8080、8080、8081 的节奏。
重要:权重是概率而非严格比例
课程里特别强调了这一点------"它并不是严格的 2:1。当请求数量足够多的时候,它接近 2:1"。
如果只发 2 次请求,完全可能两次都打到 8080,这不代表配置错了。只有请求量足够大(数百、数千次)时,统计比例才会收敛到 2:1。
这是概率算法的共性:单次结果随机,大数定律下才显现规律。验证权重配置时别只看三五次刷新。
算法原理
Nginx 用的是平滑加权轮询 (Smooth Weighted Round-Robin),每轮选出"当前权重"最高的节点,然后将其当前权重减去总权重。这样能保证分发序列均匀分布而不是前几次全给高权重节点。
以 weight=10 与 weight=5 为例,理想序列:
txt
A A B A A B A A B ...
而不是:
txt
A A A A A A A A A A B B B B B ← 会导致高权重节点瞬间被打爆
适用场景
后端机器性能不一致时。典型情况:
- 新老机器混跑(新机器
32C64G,老机器8C16G) - 灰度发布:给新版本
weight=1,稳定后逐步调大 - 一部分流量需要打到专门的机器
灰度发布的标准操作:
nginx
upstream targetserver {
server 192.168.138.101:8080 weight=9; # 稳定版,90%
server 192.168.138.102:8080 weight=1; # 灰度版,10%
}
观察灰度版无误后,逐步调成 7:3、5:5、0:10,最终全量。这比"一次性全量切换"安全得多。
四、ip_hash
配置
nginx
upstream targetserver {
ip_hash;
server 192.168.138.101:8080;
server 192.168.138.101:8081;
}
行为
对客户端 IP 地址计算哈希值,用哈希值对节点数取模,决定打到哪台:
txt
hash(client_ip) % N = 目标节点下标
只要客户端 IP 不变,就始终打到同一台服务器。
作用:会话保持
这是 ip_hash 存在的唯一理由------让同一用户的请求固定到同一台,从而复用该机器内存中的 Session。
三个明显缺点
负载倾斜 。如果大量用户来自同一个出口 IP(公司 NAT、校园网、移动基站),会全部打到同一台机器。某运营商用户特别多时,这台机器可能过载而其他机器空闲。
IP 变化导致会话丢失 。用户从 WiFi 切到 4G,IP 变了,哈希结果变了,Session 就丢了。
节点增减导致大规模失效 。节点数 N 变化时,hash(ip) % N 的结果几乎全部改变------从 3 台扩到 4 台,约 75% 的用户会落到新节点,Session 全部失效。这就是为什么**ip_hash 不适合需要频繁扩缩容的场景**。
IPv6 与 CIDR 处理
Nginx 的 ip_hash 对 IPv4 只用前三个字节 (192.168.1.x → 192.168.1)计算,这样同一 C 段的用户会分到同一台。对 IPv6 用全部 16 字节。
与 weight 的兼容性
ip_hash 与 weight 可以同时使用:
nginx
upstream targetserver {
ip_hash;
server 192.168.138.101:8080 weight=10;
server 192.168.138.101:8081 weight=5;
}
此时权重影响的是哈希环上的节点分布密度。
但 ip_hash 与 backup 不兼容 ------ip_hash 场景下 backup 会被忽略。
现代建议
ip_hash 是"外置 Session 太麻烦"时代的产物。现在的首选是 Redis 共享 Session 或 JWT (见上一篇第七节),ip_hash 只作为临时过渡。
五、least_conn(最少连接)
配置
nginx
upstream targetserver {
least_conn;
server 192.168.138.101:8080;
server 192.168.138.101:8081;
}
行为
统计每个节点当前正在处理的活跃连接数,把新请求发给连接数最少的那台。
解决的问题
轮询假设"每个请求代价相同",least_conn 不假设这一点。它看的是实际负载而非请求计数。
适用场景
请求处理耗时差异大的场景:
- 同一个接口,有的请求查缓存(10ms),有的查数据库(2s)
- 长连接、长轮询、
WebSocket - 大文件上传下载
#mermaid-svg-FHtpOU2WqyYiDpHX{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-FHtpOU2WqyYiDpHX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FHtpOU2WqyYiDpHX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FHtpOU2WqyYiDpHX .error-icon{fill:#552222;}#mermaid-svg-FHtpOU2WqyYiDpHX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FHtpOU2WqyYiDpHX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FHtpOU2WqyYiDpHX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FHtpOU2WqyYiDpHX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FHtpOU2WqyYiDpHX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FHtpOU2WqyYiDpHX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FHtpOU2WqyYiDpHX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FHtpOU2WqyYiDpHX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FHtpOU2WqyYiDpHX .marker.cross{stroke:#333333;}#mermaid-svg-FHtpOU2WqyYiDpHX svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FHtpOU2WqyYiDpHX p{margin:0;}#mermaid-svg-FHtpOU2WqyYiDpHX .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FHtpOU2WqyYiDpHX .cluster-label text{fill:#333;}#mermaid-svg-FHtpOU2WqyYiDpHX .cluster-label span{color:#333;}#mermaid-svg-FHtpOU2WqyYiDpHX .cluster-label span p{background-color:transparent;}#mermaid-svg-FHtpOU2WqyYiDpHX .label text,#mermaid-svg-FHtpOU2WqyYiDpHX span{fill:#333;color:#333;}#mermaid-svg-FHtpOU2WqyYiDpHX .node rect,#mermaid-svg-FHtpOU2WqyYiDpHX .node circle,#mermaid-svg-FHtpOU2WqyYiDpHX .node ellipse,#mermaid-svg-FHtpOU2WqyYiDpHX .node polygon,#mermaid-svg-FHtpOU2WqyYiDpHX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FHtpOU2WqyYiDpHX .rough-node .label text,#mermaid-svg-FHtpOU2WqyYiDpHX .node .label text,#mermaid-svg-FHtpOU2WqyYiDpHX .image-shape .label,#mermaid-svg-FHtpOU2WqyYiDpHX .icon-shape .label{text-anchor:middle;}#mermaid-svg-FHtpOU2WqyYiDpHX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FHtpOU2WqyYiDpHX .rough-node .label,#mermaid-svg-FHtpOU2WqyYiDpHX .node .label,#mermaid-svg-FHtpOU2WqyYiDpHX .image-shape .label,#mermaid-svg-FHtpOU2WqyYiDpHX .icon-shape .label{text-align:center;}#mermaid-svg-FHtpOU2WqyYiDpHX .node.clickable{cursor:pointer;}#mermaid-svg-FHtpOU2WqyYiDpHX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FHtpOU2WqyYiDpHX .arrowheadPath{fill:#333333;}#mermaid-svg-FHtpOU2WqyYiDpHX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FHtpOU2WqyYiDpHX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FHtpOU2WqyYiDpHX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FHtpOU2WqyYiDpHX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FHtpOU2WqyYiDpHX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FHtpOU2WqyYiDpHX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FHtpOU2WqyYiDpHX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FHtpOU2WqyYiDpHX .cluster text{fill:#333;}#mermaid-svg-FHtpOU2WqyYiDpHX .cluster span{color:#333;}#mermaid-svg-FHtpOU2WqyYiDpHX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FHtpOU2WqyYiDpHX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FHtpOU2WqyYiDpHX rect.text{fill:none;stroke-width:0;}#mermaid-svg-FHtpOU2WqyYiDpHX .icon-shape,#mermaid-svg-FHtpOU2WqyYiDpHX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FHtpOU2WqyYiDpHX .icon-shape p,#mermaid-svg-FHtpOU2WqyYiDpHX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FHtpOU2WqyYiDpHX .icon-shape .label rect,#mermaid-svg-FHtpOU2WqyYiDpHX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FHtpOU2WqyYiDpHX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FHtpOU2WqyYiDpHX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FHtpOU2WqyYiDpHX :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 新请求发给它
Nginx
least_conn
8080
活跃连接 3
8081
活跃连接 12
8082
活跃连接 1
与 weight 配合
nginx
upstream targetserver {
least_conn;
server 192.168.138.101:8080 weight=2;
server 192.168.138.101:8081 weight=1;
}
此时比较的不是绝对连接数,而是 连接数 / 权重 的比值,让高权重机器承担相对更多的连接。
局限
连接数不等于真实负载 。一个连接可能在处理耗时 5 秒的报表,也可能空闲等待。least_conn 无法区分。真正的负载指标是 CPU、响应时间------那需要 fair 策略。
六、url_hash
配置
Nginx 内置的是通用的 hash 指令,可以对任意变量哈希:
nginx
upstream targetserver {
hash $request_uri;
server 192.168.138.101:8080;
server 192.168.138.101:8081;
}
hash $request_uri 即按请求 URI 哈希,效果等同于 url_hash。
也可以对其他变量哈希:
nginx
upstream targetserver {
hash $request_uri consistent; # consistent 启用一致性哈希
server 192.168.138.101:8080;
server 192.168.138.101:8081;
}
行为
对请求的 URL 计算哈希,相同 URL 的请求固定打到同一台服务器。
作用:提升缓存命中率
这是 url_hash 的核心价值。假设每台后端都有本地缓存(如 Caffeine、本地 Guava Cache):
- 用轮询:
GET /dish/123第一次到8080缓存了,第二次到8081又要重新查库 - 用
url_hash:GET /dish/123永远到同一台,缓存命中率高
#mermaid-svg-qH9p10EQJFbUmgsU{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qH9p10EQJFbUmgsU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qH9p10EQJFbUmgsU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qH9p10EQJFbUmgsU .error-icon{fill:#552222;}#mermaid-svg-qH9p10EQJFbUmgsU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qH9p10EQJFbUmgsU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qH9p10EQJFbUmgsU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qH9p10EQJFbUmgsU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qH9p10EQJFbUmgsU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qH9p10EQJFbUmgsU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qH9p10EQJFbUmgsU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qH9p10EQJFbUmgsU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qH9p10EQJFbUmgsU .marker.cross{stroke:#333333;}#mermaid-svg-qH9p10EQJFbUmgsU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qH9p10EQJFbUmgsU p{margin:0;}#mermaid-svg-qH9p10EQJFbUmgsU .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-qH9p10EQJFbUmgsU .cluster-label text{fill:#333;}#mermaid-svg-qH9p10EQJFbUmgsU .cluster-label span{color:#333;}#mermaid-svg-qH9p10EQJFbUmgsU .cluster-label span p{background-color:transparent;}#mermaid-svg-qH9p10EQJFbUmgsU .label text,#mermaid-svg-qH9p10EQJFbUmgsU span{fill:#333;color:#333;}#mermaid-svg-qH9p10EQJFbUmgsU .node rect,#mermaid-svg-qH9p10EQJFbUmgsU .node circle,#mermaid-svg-qH9p10EQJFbUmgsU .node ellipse,#mermaid-svg-qH9p10EQJFbUmgsU .node polygon,#mermaid-svg-qH9p10EQJFbUmgsU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qH9p10EQJFbUmgsU .rough-node .label text,#mermaid-svg-qH9p10EQJFbUmgsU .node .label text,#mermaid-svg-qH9p10EQJFbUmgsU .image-shape .label,#mermaid-svg-qH9p10EQJFbUmgsU .icon-shape .label{text-anchor:middle;}#mermaid-svg-qH9p10EQJFbUmgsU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-qH9p10EQJFbUmgsU .rough-node .label,#mermaid-svg-qH9p10EQJFbUmgsU .node .label,#mermaid-svg-qH9p10EQJFbUmgsU .image-shape .label,#mermaid-svg-qH9p10EQJFbUmgsU .icon-shape .label{text-align:center;}#mermaid-svg-qH9p10EQJFbUmgsU .node.clickable{cursor:pointer;}#mermaid-svg-qH9p10EQJFbUmgsU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-qH9p10EQJFbUmgsU .arrowheadPath{fill:#333333;}#mermaid-svg-qH9p10EQJFbUmgsU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qH9p10EQJFbUmgsU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qH9p10EQJFbUmgsU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qH9p10EQJFbUmgsU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-qH9p10EQJFbUmgsU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qH9p10EQJFbUmgsU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-qH9p10EQJFbUmgsU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qH9p10EQJFbUmgsU .cluster text{fill:#333;}#mermaid-svg-qH9p10EQJFbUmgsU .cluster span{color:#333;}#mermaid-svg-qH9p10EQJFbUmgsU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-qH9p10EQJFbUmgsU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-qH9p10EQJFbUmgsU rect.text{fill:none;stroke-width:0;}#mermaid-svg-qH9p10EQJFbUmgsU .icon-shape,#mermaid-svg-qH9p10EQJFbUmgsU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qH9p10EQJFbUmgsU .icon-shape p,#mermaid-svg-qH9p10EQJFbUmgsU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-qH9p10EQJFbUmgsU .icon-shape .label rect,#mermaid-svg-qH9p10EQJFbUmgsU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qH9p10EQJFbUmgsU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-qH9p10EQJFbUmgsU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-qH9p10EQJFbUmgsU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 轮询
/dish/123
8080
缓存未命中,查库
/dish/123
8081
缓存未命中,查库
/dish/123
8080
缓存命中
#mermaid-svg-sRdzdcWLzhuSWmYm{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-sRdzdcWLzhuSWmYm .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-sRdzdcWLzhuSWmYm .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-sRdzdcWLzhuSWmYm .error-icon{fill:#552222;}#mermaid-svg-sRdzdcWLzhuSWmYm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-sRdzdcWLzhuSWmYm .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-sRdzdcWLzhuSWmYm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-sRdzdcWLzhuSWmYm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-sRdzdcWLzhuSWmYm .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-sRdzdcWLzhuSWmYm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-sRdzdcWLzhuSWmYm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-sRdzdcWLzhuSWmYm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-sRdzdcWLzhuSWmYm .marker.cross{stroke:#333333;}#mermaid-svg-sRdzdcWLzhuSWmYm svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-sRdzdcWLzhuSWmYm p{margin:0;}#mermaid-svg-sRdzdcWLzhuSWmYm .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-sRdzdcWLzhuSWmYm .cluster-label text{fill:#333;}#mermaid-svg-sRdzdcWLzhuSWmYm .cluster-label span{color:#333;}#mermaid-svg-sRdzdcWLzhuSWmYm .cluster-label span p{background-color:transparent;}#mermaid-svg-sRdzdcWLzhuSWmYm .label text,#mermaid-svg-sRdzdcWLzhuSWmYm span{fill:#333;color:#333;}#mermaid-svg-sRdzdcWLzhuSWmYm .node rect,#mermaid-svg-sRdzdcWLzhuSWmYm .node circle,#mermaid-svg-sRdzdcWLzhuSWmYm .node ellipse,#mermaid-svg-sRdzdcWLzhuSWmYm .node polygon,#mermaid-svg-sRdzdcWLzhuSWmYm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-sRdzdcWLzhuSWmYm .rough-node .label text,#mermaid-svg-sRdzdcWLzhuSWmYm .node .label text,#mermaid-svg-sRdzdcWLzhuSWmYm .image-shape .label,#mermaid-svg-sRdzdcWLzhuSWmYm .icon-shape .label{text-anchor:middle;}#mermaid-svg-sRdzdcWLzhuSWmYm .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-sRdzdcWLzhuSWmYm .rough-node .label,#mermaid-svg-sRdzdcWLzhuSWmYm .node .label,#mermaid-svg-sRdzdcWLzhuSWmYm .image-shape .label,#mermaid-svg-sRdzdcWLzhuSWmYm .icon-shape .label{text-align:center;}#mermaid-svg-sRdzdcWLzhuSWmYm .node.clickable{cursor:pointer;}#mermaid-svg-sRdzdcWLzhuSWmYm .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-sRdzdcWLzhuSWmYm .arrowheadPath{fill:#333333;}#mermaid-svg-sRdzdcWLzhuSWmYm .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-sRdzdcWLzhuSWmYm .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-sRdzdcWLzhuSWmYm .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sRdzdcWLzhuSWmYm .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-sRdzdcWLzhuSWmYm .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sRdzdcWLzhuSWmYm .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-sRdzdcWLzhuSWmYm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-sRdzdcWLzhuSWmYm .cluster text{fill:#333;}#mermaid-svg-sRdzdcWLzhuSWmYm .cluster span{color:#333;}#mermaid-svg-sRdzdcWLzhuSWmYm div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-sRdzdcWLzhuSWmYm .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-sRdzdcWLzhuSWmYm rect.text{fill:none;stroke-width:0;}#mermaid-svg-sRdzdcWLzhuSWmYm .icon-shape,#mermaid-svg-sRdzdcWLzhuSWmYm .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sRdzdcWLzhuSWmYm .icon-shape p,#mermaid-svg-sRdzdcWLzhuSWmYm .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-sRdzdcWLzhuSWmYm .icon-shape .label rect,#mermaid-svg-sRdzdcWLzhuSWmYm .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sRdzdcWLzhuSWmYm .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-sRdzdcWLzhuSWmYm .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-sRdzdcWLzhuSWmYm :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} url_hash
/dish/123
8080
缓存未命中,查库
/dish/123
/dish/123
后续全部命中缓存
局限
热点 URL 会造成倾斜 。如果 /index 占了 80% 的流量,负责它的那台机器会过载。
节点增减同样导致大规模失效 ------除非启用 consistent(一致性哈希)。
一致性哈希
hash $request_uri consistent; 开启一致性哈希后,节点增减只影响 1/N 的键,而不是全部。这是分布式缓存的标准方案:
| 方式 | 3 台扩到 4 台时受影响的 URL 比例 |
|---|---|
| 普通哈希 | 约 75% |
| 一致性哈希 | 约 25% |
生产上用 hash 时强烈建议加 consistent。
注意
url_hash 与 ip_hash 不能同时使用------二者都是"哈希选节点"策略,语义冲突。
七、fair(响应时间)
配置
nginx
upstream targetserver {
fair;
server 192.168.138.101:8080;
server 192.168.138.101:8081;
}
行为
统计每个节点的平均响应时间,优先把请求发给响应最快的那台。
重要:这是第三方模块
fair 不是 Nginx 内置的 ,来自 nginx-upstream-fair 模块。直接用会报:
txt
nginx: [emerg] unknown directive "fair"
需要下载模块源码,在编译 Nginx 时加进去:
bash
./configure --prefix=/usr/local/nginx --add-module=/usr/local/src/nginx-upstream-fair
make && make install
为什么课程里"不演示"
正是因为它需要额外编译,环境依赖较重。课程只要求"了解即可"。
现代替代方案
现在的生产环境更倾向于用商业版 Nginx Plus 的主动健康检查 ,或者把负载均衡交给服务治理框架 (Spring Cloud LoadBalancer、 Nacos)或 Service Mesh (Istio),它们能基于更丰富的指标(CPU、错误率、延迟分位数)做决策,而不仅仅是响应时间。
八、策略对比与选型
对比总表
| 策略 | 依据 | 会话保持 | 负载均匀性 | 节点增减影响 | 需额外模块 |
|---|---|---|---|---|---|
| 轮询 | 请求顺序 | ✗ | 高 | 无影响 | ✗ |
weight |
权重比例 | ✗ | 中(按权重) | 无影响 | ✗ |
ip_hash |
客户端 IP |
✓ | 低(可能倾斜) | 大规模失效 | ✗ |
least_conn |
活跃连接数 | ✗ | 高 | 无影响 | ✗ |
url_hash |
请求 URL |
✗(URL 亲和) |
低(热点倾斜) | 大规模失效(consistent 可缓解) |
✗ |
fair |
响应时间 | ✗ | 最高 | 无影响 | ✓ |
选型决策树
#mermaid-svg-A4FFc4mXFWdSptk1{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-A4FFc4mXFWdSptk1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-A4FFc4mXFWdSptk1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-A4FFc4mXFWdSptk1 .error-icon{fill:#552222;}#mermaid-svg-A4FFc4mXFWdSptk1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-A4FFc4mXFWdSptk1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-A4FFc4mXFWdSptk1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-A4FFc4mXFWdSptk1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-A4FFc4mXFWdSptk1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-A4FFc4mXFWdSptk1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-A4FFc4mXFWdSptk1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-A4FFc4mXFWdSptk1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-A4FFc4mXFWdSptk1 .marker.cross{stroke:#333333;}#mermaid-svg-A4FFc4mXFWdSptk1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-A4FFc4mXFWdSptk1 p{margin:0;}#mermaid-svg-A4FFc4mXFWdSptk1 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-A4FFc4mXFWdSptk1 .cluster-label text{fill:#333;}#mermaid-svg-A4FFc4mXFWdSptk1 .cluster-label span{color:#333;}#mermaid-svg-A4FFc4mXFWdSptk1 .cluster-label span p{background-color:transparent;}#mermaid-svg-A4FFc4mXFWdSptk1 .label text,#mermaid-svg-A4FFc4mXFWdSptk1 span{fill:#333;color:#333;}#mermaid-svg-A4FFc4mXFWdSptk1 .node rect,#mermaid-svg-A4FFc4mXFWdSptk1 .node circle,#mermaid-svg-A4FFc4mXFWdSptk1 .node ellipse,#mermaid-svg-A4FFc4mXFWdSptk1 .node polygon,#mermaid-svg-A4FFc4mXFWdSptk1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-A4FFc4mXFWdSptk1 .rough-node .label text,#mermaid-svg-A4FFc4mXFWdSptk1 .node .label text,#mermaid-svg-A4FFc4mXFWdSptk1 .image-shape .label,#mermaid-svg-A4FFc4mXFWdSptk1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-A4FFc4mXFWdSptk1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-A4FFc4mXFWdSptk1 .rough-node .label,#mermaid-svg-A4FFc4mXFWdSptk1 .node .label,#mermaid-svg-A4FFc4mXFWdSptk1 .image-shape .label,#mermaid-svg-A4FFc4mXFWdSptk1 .icon-shape .label{text-align:center;}#mermaid-svg-A4FFc4mXFWdSptk1 .node.clickable{cursor:pointer;}#mermaid-svg-A4FFc4mXFWdSptk1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-A4FFc4mXFWdSptk1 .arrowheadPath{fill:#333333;}#mermaid-svg-A4FFc4mXFWdSptk1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-A4FFc4mXFWdSptk1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-A4FFc4mXFWdSptk1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-A4FFc4mXFWdSptk1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-A4FFc4mXFWdSptk1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-A4FFc4mXFWdSptk1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-A4FFc4mXFWdSptk1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-A4FFc4mXFWdSptk1 .cluster text{fill:#333;}#mermaid-svg-A4FFc4mXFWdSptk1 .cluster span{color:#333;}#mermaid-svg-A4FFc4mXFWdSptk1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-A4FFc4mXFWdSptk1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-A4FFc4mXFWdSptk1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-A4FFc4mXFWdSptk1 .icon-shape,#mermaid-svg-A4FFc4mXFWdSptk1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-A4FFc4mXFWdSptk1 .icon-shape p,#mermaid-svg-A4FFc4mXFWdSptk1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-A4FFc4mXFWdSptk1 .icon-shape .label rect,#mermaid-svg-A4FFc4mXFWdSptk1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-A4FFc4mXFWdSptk1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-A4FFc4mXFWdSptk1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-A4FFc4mXFWdSptk1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否(新老混跑/灰度)
是
是
能
不能(遗留系统)
否
是(报表/长连接)
否
是
否
开始
后端机器性能一致?
weight
需要会话保持?
能否改造成 Redis Session / JWT?
改造后回到轮询
★ 推荐
ip_hash
注意扩容问题
请求耗时差异大?
least_conn
后端有本地缓存?
hash $request_uri consistent
轮询(默认)
外卖平台的推荐配置
结合项目特点(常规 CRUD、耗时较均匀、Session 外置 Redis):
nginx
upstream reggie_server {
# 默认轮询即可,两配置相同的机器
server 192.168.138.101:8080 weight=1 max_fails=3 fail_timeout=30s;
server 192.168.138.102:8080 weight=1 max_fails=3 fail_timeout=30s;
}
配合 Session 外置 Redis,不需要 ip_hash,也不需要 least_conn。
如果要做灰度:
nginx
upstream reggie_server {
server 192.168.138.101:8080 weight=9; # 稳定版
server 192.168.138.102:8080 weight=1; # 灰度版
}
九、验证权重策略的正确方法
不要靠手动刷新三五次判断,用脚本统计:
bash
#!/bin/bash
# 统计 60 次请求在各节点的分布
URL=http://192.168.138.100:8080/hello
echo "发送 60 次请求,统计分布..."
for i in $(seq 1 60); do
curl -s ${URL}
echo ""
done | sort | uniq -c | sort -rn
输出示例(weight=10:5):
txt
40 8080
20 8081
接近 2:1,说明权重配置生效。
如果只发 3 次就下结论,很可能得到 3:0 或 1:2 这种误导性结果。
API 速览
| 指令 / 参数 | 作用 | 备注 |
|---|---|---|
| (无) | 轮询,默认策略 | |
weight=<n> |
权重,默认 1 |
概率性,需大量请求才显现比例 |
ip_hash; |
按客户端 IP 哈希 |
会话保持;与 backup 不兼容 |
least_conn; |
按最少活跃连接 | 适配耗时差异大的场景 |
hash $request_uri; |
按请求 URI 哈希 |
提升本地缓存命中率 |
hash $request_uri consistent; |
一致性哈希 | 节点增减只影响 1/N |
fair; |
按响应时间 | 第三方模块,需重新编译 |
zone <name> <size>; |
共享内存区,存节点状态 | Nginx Plus 动态配置用 |
curl -s <url> |
静默请求 | 配合循环做分布统计 |
| `sort | uniq -c` | 统计结果分布 |
官方文档
Nginxupstream模块(含ip_hash、least_conn、hash):https://nginx.org/en/docs/http/ngx_http_upstream_module.htmlNginx负载均衡官方指南:https://nginx.org/en/docs/http/load_balancing.htmlnginx-upstream-fair模块:https://github.com/gnosek/nginx-upstream-fair- 一致性哈希(
Karger论文):https://www.cs.princeton.edu/courses/archive/fall09/cos518/papers/chash.pdf Spring Session Redis:https://docs.spring.io/spring-session/reference/guides/boot-redis.html
总结
六种策略按"决策依据"可分为三类 :无状态计数类(轮询、weight)、哈希亲和类(ip_hash、url_hash)、实时负载类(least_conn、fair)。
默认轮询适用于大多数场景。只要后端机器配置一致、请求耗时接近,轮询就是最均匀也最简单的选择------这也是它作为默认值的原因。
weight 是概率性的,不是严格比例 。10:5 只在大请求量下趋近 2:1,只发两三次请求就下结论会得出错误判断。验证时用循环发几十上百次再统计分布。它的真正价值在于灰度发布------给新版本小权重,逐步放量。
ip_hash 和 url_hash 都有"节点增减导致大规模失效"的致命问题 。hash(ip) % N 在 N 变化时几乎全部重排。用 hash 时务必加 consistent 启用一致性哈希,把影响面从约 75% 降到约 25%。
fair 不是内置指令 ,来自第三方模块,直接用会报 unknown directive "fair",需要下载源码重新编译 Nginx。这也是课程只讲解不演示的原因。
选型的本质是权衡 :要会话保持就得接受负载倾斜和扩容困难;要缓存亲和就得接受热点倾斜;要实时负载就得接受更高的实现复杂度。现代架构的正确方向是把状态外置 (Redis Session / JWT),让后端无状态,从而可以自由使用最简单的轮询策略。
到这里 Nginx 的三大应用(静态资源、反向代理、负载均衡)全部讲完。下一篇进入 day03 的前后端分离开发:先讲开发模式对比、YApi 接口管理平台,再讲 Swagger 的接入与常用注解。