Nginx负载均衡、健康判断与重试全过程
负载均衡不是“轮流找一台服务器”这么简单。一次代理请求至少要经历:选择候选节点、检查节点是否可用、建立或复用连接、发送请求、判断失败类型、决定是否重试、更新节点运行状态,再把响应返回客户端。任何一步设计错误,都可能造成流量倾斜、重复下单、重试风暴或数据库雪崩。
学习目标
学完后应能:
- 区分四层负载均衡和七层HTTP负载均衡。
- 解释Nginx upstream从配置解析到请求选节点的完整过程。
- 区分加权轮询、least_conn、ip_hash、通用hash和随机两选一。
- 解释weight为什么不是每十个请求严格按比例分配。
- 说明被动失败判断、
max_fails、fail_timeout和主动健康检查的区别。 - 解释
proxy_next_upstream何时重试以及非幂等请求的重复执行风险。 - 区分upstream keepalive缓存、活跃连接数和后端总容量。
- 解释Session粘滞为什么不能代替共享Session和无状态设计。
- 处理扩缩容、DNS变化、慢节点、连接倾斜和重试风暴。
- 使用访问日志与upstream变量定位502、504、流量不均和单节点P99异常。
一、负载均衡解决什么,不解决什么
flowchart TD
A["客户端只访问统一入口"] --> B["Nginx"]
B --> C["order-api-1"]
B --> D["order-api-2"]
B --> E["order-api-3"]它主要解决:
- 把请求分给多个后端实例。
- 横向扩容入口后的业务处理能力。
- 在部分节点失败时尝试其他节点。
- 按权重、连接数或Key实现不同分配策略。
- 隐藏后端实例地址。
它不自动解决:
- Nginx自身单点。
- 数据库容量不足。
- Java线程池、连接池耗尽。
- Session共享。
- 非幂等请求重试。
- 数据一致性。
- 所有类型的健康检查。
- 节点跨机房和跨可用区故障域。
二、四层和七层负载均衡
| 类型 | 主要依据 | 能看到什么 | 常见用途 |
|---|---|---|---|
| 四层 | IP、端口、TCP/UDP连接 | 不必解析完整HTTP | 高吞吐TCP转发、TLS透传 |
| 七层 | Host、URI、Header、Cookie等HTTP信息 | 理解HTTP请求和响应 | API路由、Header治理、缓存、限流 |
本页主要讲Nginx HTTP upstream,即七层代理。Nginx stream模块可做TCP/UDP代理,但健康、重试和日志语义不同,不能把HTTP指令直接搬过去。
三、一次请求的完整选节点过程
flowchart TD
A["请求命中proxy_pass"] --> B["找到upstream配置"]
B --> C["按算法生成候选节点"]
C --> D["跳过down或暂时不可用节点"]
D --> E["复用空闲连接或新建TCP连接"]
E --> F["发送上游请求"]
F --> G{"是否获得可接受响应"}
G -- "是" --> H["转发响应并更新统计"]
G -- "否" --> I["按失败类型更新节点状态"]
I --> J{"重试策略、次数和时间是否允许"}
J -- "是" --> K["选择另一个候选节点"]
K --> E
J -- "否" --> L["向客户端返回502/504等错误"]选择算法只负责“先尝试谁”,并不单独决定:
- 连接能否建立。
- 请求是否已经写给后端。
- 后端是否已经提交事务。
- 哪些响应算失败。
- 是否可以重试。
- 客户端最终看到什么。
四、最小upstream配置
upstream order_api {
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
server {
listen 80;
server_name api.example.com;
location /api/ {
proxy_pass http://order_api/;
}
}启动或reload时,Nginx解析upstream名称、节点地址和参数,为每个Worker建立运行时选择结构。域名解析时机和动态更新能力取决于配置写法、Nginx版本、resolver和商业/开源能力,不能假定DNS变化一定实时生效。
五、默认加权轮询
未声明其他算法时,使用加权轮询思路:
upstream order_api {
server 10.0.1.11:8080 weight=1;
server 10.0.1.12:8080 weight=1;
}长期、节点持续健康且请求成本接近时,两台节点获得的请求数量趋近1:1。
5.1 为什么不是简单下标加一
现代Nginx加权轮询会维护节点权重状态,让不同权重在短窗口内也尽量平滑分布,而不是先把大权重节点连续打满再切换。
例如权重:
A=5,B=1,C=1理想长期比例约5:1:1,但不保证任意连续7个请求都严格如此,因为还受到:
- 节点失败和恢复。
- 多Worker各自选择状态。
- reload。
- 并发时序。
- 请求重试。
- 长连接和请求耗时差异。
六、weight表示能力比例,不是绝对QPS
upstream order_api {
server 10.0.1.11:8080 weight=3;
server 10.0.1.12:8080 weight=1;
}在稳定条件下,请求数量趋近3:1。但weight不理解每个请求成本:
/health可能1ms。/report/export可能30s。- 一个请求只查缓存。
- 另一个请求执行复杂SQL。
请求数3:1不等于CPU、内存和数据库负载3:1。业务差异很大时,还需要路由拆分、限流、异步化和容量模型。
七、least_conn最少连接
upstream order_api {
least_conn;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}它优先选择当前活跃连接相对更少的节点,适合请求耗时差异较大、长连接较多的场景。
7.1 为什么不等于最小CPU
连接少的节点不一定最空闲:
- 一个连接正在执行超重报表。
- 大量请求已进入Java内部队列,但Nginx连接状态不能完整代表线程池压力。
- GC暂停时连接数可能不高但响应极慢。
- 数据库连接池已经耗尽。
least_conn只使用Nginx可观察的连接状态,不是全链路负载感知。
7.2 权重仍可能参与
节点配置weight时,最少连接算法会结合权重比较相对负载,不应简单理解为“谁绝对连接数最小就选谁”。
八、ip_hash会话粘滞
upstream order_api {
ip_hash;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}相同客户端地址通常映射到同一节点,可降低本地Session不共享带来的影响。
8.1 为什么会严重倾斜
大量用户可能经过同一个:
- 企业NAT出口。
- 移动运营商代理。
- CDN或上层负载均衡。
- API Gateway。
Nginx如果看到的地址都是同一个代理IP,很多用户会被哈希到同一节点。必须先建立可信真实IP链路,但即使拿到真实IP,移动网络切换也会改变地址。
8.2 为什么不能解决Session设计
节点宕机、扩缩容或映射变化时,请求仍会切到其他节点。本地Session可能丢失。商业系统更常用:
- 无状态Token。
- Redis等共享Session。
- 业务状态进入数据库或专门状态服务。
粘滞只能减少切换,不提供Session高可用和一致性。
九、通用hash与一致性hash
upstream cache_backend {
hash $request_uri consistent;
server 10.0.2.11:8080;
server 10.0.2.12:8080;
server 10.0.2.13:8080;
}可按URI、租户、用户等Key映射节点。consistent用于降低节点增减时Key大规模迁移,常见于缓存分片或希望同Key落到同节点的场景。
风险:
- 热Key仍会压到单节点。
- Key分布不均会造成倾斜。
- 节点容量不同需要权重与数据设计。
- 后端是有状态分片时,Nginx哈希不能代替数据迁移协议。
- Key包含敏感或不稳定值会影响审计与命中。
旧教程中的 url_hash、hash_method crc32常来自第三方模块;现代标准能力应使用当前Nginx版本支持的 hash ... consistent,并先执行 nginx -t。
十、随机与随机两选一
部分现代Nginx版本支持类似:
upstream order_api {
random two least_conn;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
server 10.0.1.13:8080;
}思路是随机抽取少量候选,再从候选中按连接状态选择,减少超大集群全量比较成本,同时获得较好负载分布。实际语法与版本支持必须通过目标版本文档、nginx -V和 nginx -t确认。
十一、fair和least_time不能混为一谈
旧文章常写:
fair;它通常来自第三方模块,不是所有开源Nginx二进制都支持。NGINX Plus还提供基于响应时间等指标的商业能力,例如least_time,但也不能假定社区版具备。
遇到:
unknown directive "fair"应检查:
nginx -V
nginx -T确认模块来源、版本、维护状态和供应链风险,而不是下载来历不明的二进制覆盖生产Nginx。
十二、节点参数逐项理解
upstream order_api {
server 10.0.1.11:8080 weight=2 max_fails=3 fail_timeout=10s max_conns=200;
server 10.0.1.12:8080 weight=1 max_fails=3 fail_timeout=10s max_conns=100;
server 10.0.1.13:8080 backup;
}| 参数 | 主要含义 | 常见误区 |
|---|---|---|
| weight | 参与选节点的相对权重 | 不代表绝对QPS或CPU比例 |
| max_fails | 在窗口内允许的失败尝试次数 | 不会主动定时探测业务健康 |
| fail_timeout | 失败统计窗口及暂时不可用时间相关边界 | 不是所有请求总超时 |
| max_conns | 限制到节点的活跃连接数量 | 空闲keepalive和多Worker统计边界要按版本验证 |
| backup | 主节点不可用时才使用 | 某些算法不兼容 |
| down | 人工标记不参与选择 | 修改后要reload并观察 |
指令兼容性会随算法、版本和开源/商业发行版变化,配置前必须查目标版本文档。
十三、被动健康判断是什么
开源Nginx常用请求流量本身做被动判断:
真实请求选择节点
→ 建连失败、超时或命中配置的失败条件
→ 记录一次失败尝试
→ 窗口内达到max_fails
→ 节点在fail_timeout相关时间内暂时不参与普通选择
→ 之后重新尝试节点它不是独立健康检查线程定时请求 /health。没有业务请求时,就没有新的被动探测证据。
13.1 哪些响应算失败
由协议模块和 proxy_next_upstream 等配置共同决定。连接错误、超时通常属于失败;HTTP 500、502、503、504等是否触发下一节点,要看配置。普通业务404通常不应把节点判死,因为它可能是合法业务响应。
13.2 单节点upstream边界
只有一个节点时,为避免把唯一节点永久判为不可用,某些失败参数会按特殊规则处理。不能用单节点实验推断多节点摘除行为。
十四、主动健康检查不是开源版通用内置
旧配置:
check interval=3000 rise=2 fall=5 timeout=1000 type=http;通常来自第三方健康检查模块,不是标准开源Nginx通用指令。NGINX Plus提供商业主动健康检查能力;不同发行版或平台也可能提供自己的模块。
主动检查思路:
flowchart TD
A["定时向健康URI发送探测"] --> B{"响应状态和内容是否符合规则"}
B -- "连续失败达到阈值" --> C["标记不可用"]
B -- "成功" --> D["累计恢复成功次数"]
D --> E{"达到恢复阈值"}
E -- "是" --> F["重新加入流量"]健康URI也不能只返回进程存活。应区分:
- liveness:进程是否活着。
- readiness:是否准备接业务流量。
- dependency:关键依赖是否可用。
- 外部业务探测:完整入口是否成功。
把所有下游都塞进readiness可能引发级联摘流;完全不检查关键初始化又会让未准备节点接流量。
十五、max_fails与fail_timeout实例
server 10.0.1.11:8080 max_fails=3 fail_timeout=10s;可理解为:在相关10秒窗口内,若符合失败条件的尝试达到3次,节点会在一段相关时间内被视为不可用,之后再尝试恢复。精确计时和状态共享边界以当前版本实现与文档为准。
它不是:
- 每10秒主动检查一次。
- 节点失败后永久删除。
- 所有HTTP 4xx/5xx都计失败。
- 保证请求不会返回错误。
- 保证后端业务没有执行。
十六、proxy_next_upstream决定哪些失败可换节点
示例:
location /api/ {
proxy_pass http://order_api;
proxy_connect_timeout 2s;
proxy_read_timeout 15s;
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 2;
proxy_next_upstream_timeout 5s;
}含义:
proxy_next_upstream:哪些情况允许尝试下一节点。tries:尝试次数上限,具体计数包含初次尝试的语义要按版本文档确认。timeout:跨多次尝试的总时间边界之一。
重试只能在仍可安全切换的阶段发生。响应已经部分发送给客户端后,通常不能透明改换另一个后端重新返回完整响应。
十七、为什么重试可能造成重复下单
sequenceDiagram
participant N as Nginx
participant A as App A
participant D as Database
participant B as App B
N->>A: POST /orders
A->>D: 插入订单并提交
D-->>A: 提交成功
A--xN: 响应返回前连接断开
N->>B: 重试同一个POST
B->>D: 再次插入订单从Nginx视角看,第一次没有拿到有效响应;从业务视角看,数据库可能已经提交。若直接重试POST,可能重复创建订单、重复扣款或重复发消息。
治理:
- 非幂等请求默认谨慎重试。
- 订单、支付使用幂等Key。
- 数据库唯一约束兜底。
- 状态机拒绝非法重复流转。
- 消息和外部调用有去重与补偿。
- 记录每次upstream尝试链路。
配置 non_idempotent 允许更多非幂等请求重试时风险更高,不能只为降低错误率而开启。
十八、GET也不一定天然安全
HTTP语义上GET应安全、幂等,但旧系统可能错误地用GET执行写操作,例如:
GET /coupon/claim
GET /task/runNginx按方法语义重试时无法理解业务副作用。因此首先要修正API设计,并在数据库与业务层实现幂等,而不是把安全完全交给代理层猜测。
十九、重试风暴如何形成
后端容量下降50%
→ 原请求开始超时
→ Nginx重试到其他节点
→ 剩余节点收到原流量加重试流量
→ 延迟进一步升高
→ 更多请求触发重试
→ 所有节点和数据库被打满限制:
- 少量明确重试次数。
- 跨尝试总超时。
- 入口限流。
- 熔断和降级。
- 后端容量保护。
- 客户端、网关、服务SDK避免层层重复重试。
如果客户端、Nginx、Feign和数据库驱动各重试3次,最坏尝试次数可能乘法放大。
二十、upstream keepalive完整边界
upstream order_api {
server 10.0.1.11:8080;
server 10.0.1.12:8080;
keepalive 32;
}
location /api/ {
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass http://order_api;
}典型过程:
请求选择节点
→ 尝试从当前Worker空闲连接缓存取连接
→ 有可用连接则复用
→ 没有则新建TCP连接
→ 请求完成
→ 连接符合条件时放回空闲缓存keepalive 32通常表示每个Worker为该upstream缓存的空闲连接数量,不是:
- 最大总连接数。
- 最大并发请求数。
- 所有Worker共享的32条连接。
- 后端连接池保护上限。
4个Worker理论上可能各有自己的空闲缓存。还要考虑活跃连接、后端keep-alive超时和连接关闭竞争。
二十一、为什么Keep-Alive可能产生连接倾斜
扩容一个新后端后,旧节点上已有大量可复用连接,新节点连接较少。短时间内:
- 旧连接继续被复用。
- 新节点尚未获得同等连接池热度。
- 长连接不会立刻迁移。
- HTTP/2单连接可承载多个stream时连接数更不能代表请求比例。
因此扩容后流量不会保证瞬时均匀,需要观察请求数、连接数和响应时间,而不是只看upstream配置已包含新IP。
二十二、max_conns不是完整后端保护
server 10.0.1.11:8080 max_conns=200;它用于限制到节点的活跃连接数量,但:
- 多Worker是否共享计数取决于共享zone、版本和配置。
- 空闲keepalive连接可能不计入活跃连接,却仍占后端FD。
- HTTP/2多路复用改变连接与请求关系。
- Java线程池、队列和数据库连接池的安全并发不一定等于200。
后端保护还需应用限流、线程池隔离、连接池、熔断和数据库容量。
二十三、共享zone解决什么
部分upstream能力可使用共享内存zone,让Worker共享节点配置和运行状态:
upstream order_api {
zone order_api_zone 64k;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}是否需要zone、哪些状态共享、动态DNS和商业能力如何依赖zone,会随版本与功能变化。不能只为“性能”盲目加zone;应根据具体指令文档配置大小并验证reload和状态。
二十四、DNS与动态服务发现
静态域名:
server app.internal:8080;不一定意味着每个请求都重新查询DNS。很多配置在启动或reload时解析,后端IP变化后可能继续使用旧地址。
现代版本可结合resolver、resolve参数和共享zone实现一定动态更新,但开源/商业版本、语法和支持时间不同,必须查目标版本。
需要验证:
- DNS TTL。
- NXDOMAIN和超时。
- 多A/AAAA记录。
- 节点删除与新增。
- 旧Keep-Alive连接。
- reload行为。
- Docker/Kubernetes服务发现边界。
在Kubernetes中通常代理Service稳定地址,而不是直接把短生命周期Pod IP静态写进Nginx。
二十五、backup与故障域
upstream order_api {
server 10.0.1.11:8080;
server 10.0.1.12:8080;
server 10.0.2.11:8080 backup;
}backup在主节点不可用时参与,但它不自动保证:
- 备用节点数据与主节点一致。
- 备用机房数据库容量足够。
- 网络与DNS能切换。
- Session和缓存已预热。
- 某些hash/random算法兼容backup。
备用容量长期不接流量还可能存在配置漂移。生产应定期演练和小流量验证。
二十六、slow_start与恢复洪峰
节点恢复后如果立即按完整weight接流量,JIT、连接池、缓存和类加载可能尚未预热,导致再次超时。
慢启动思路:
节点恢复
→ 从较低有效权重开始
→ 逐步增加流量
→ 观察错误率和延迟
→ 达到目标权重slow_start等具体能力可能属于NGINX Plus或受版本/算法限制,开源环境可由编排平台金丝雀、权重分阶段变更或上层负载均衡实现。不要在不支持的二进制上直接复制指令。
二十七、会话与Cookie粘滞
商业版或第三方模块可能支持Cookie型sticky会话。它比IP哈希更接近用户会话,但仍有:
- Cookie丢失和跨域限制。
- 节点故障后的重新映射。
- 扩缩容与版本发布。
- 恶意固定或篡改风险。
- 本地Session数据丢失。
入口粘滞不是业务状态存储。核心系统应尽量无状态或使用共享状态服务。
二十八、为什么单个慢节点会拖高整体P99
三台节点:
A P99=50ms
B P99=60ms
C P99=5s轮询仍把约三分之一请求送到C,整体尾延迟会明显升高。C不一定触发被动摘除,因为它最终在超时前返回200。
治理:
- 观察每个
$upstream_addr的响应时间分布。 - 应用自身健康和线程池指标。
- 设置合理超时,但避免误杀。
- 主动健康/异常实例摘流。
- 权重降低或发布回滚。
- 查慢SQL、GC、CPU throttling和依赖延迟。
“节点没宕机”不等于“节点健康”。
二十九、日志必须记录每次upstream尝试
log_format upstream_main
'$request_id $remote_addr "$request" status=$status '
'request_time=$request_time '
'upstream_addr=$upstream_addr '
'upstream_status=$upstream_status '
'connect=$upstream_connect_time '
'header=$upstream_header_time '
'response=$upstream_response_time';发生重试时,这些upstream变量可能以逗号或分隔形式记录多次尝试。应保留顺序关系,才能判断:
先访问A连接超时
→ 再访问B返回200
→ 客户端最终看到200只看最终 $status=200 会掩盖A正在故障和重试流量。
三十、流量不均排查
- 统计每个upstream地址请求数,不只看连接数。
- 检查算法和weight。
- 检查ip_hash或hash Key分布。
- 检查真实IP是否被上层代理统一。
- 检查失败、重试和节点暂时不可用状态。
- 检查Worker、共享zone与reload。
- 检查长连接和Keep-Alive复用。
- 检查新节点是否已被DNS解析并加入运行状态。
- 对比每节点响应时间和业务请求成本。
不能只看“机器CPU不一样”就认定算法失效,节点硬件、JVM、流量类型和缓存命中也可能不同。
三十一、502与504排查
502
常见于:
- DNS解析失败。
- 连接被拒绝。
- 上游协议错误。
- 后端提前关闭。
- 无效响应。
- 所有候选节点不可用。
504
常见于:
- 建连超时。
- 后端读取超时。
- 慢SQL。
- Java线程池/连接池等待。
- 下游接口慢。
- 重试总时间过长。
使用error log和upstream时间变量区分初次节点、重试节点、连接阶段和响应阶段。
三十二、可运行三节点Demo
先启动三个本地后端示意服务,分别监听8081、8082、8083并返回自己的节点名。可以使用三个最小Spring Boot服务、三个Nginx return server或团队已有回显镜像。
Nginx配置:
events {
worker_connections 1024;
}
http {
log_format lb '$request_id upstream=$upstream_addr '
'upstream_status=$upstream_status '
'upstream_time=$upstream_response_time '
'status=$status request_time=$request_time';
access_log /dev/stdout lb;
error_log /dev/stderr notice;
upstream order_api {
least_conn;
server 127.0.0.1:8081 max_fails=2 fail_timeout=10s;
server 127.0.0.1:8082 max_fails=2 fail_timeout=10s;
server 127.0.0.1:8083 max_fails=2 fail_timeout=10s;
keepalive 16;
}
server {
listen 8090;
location / {
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header X-Request-Id $request_id;
proxy_pass http://order_api;
proxy_connect_timeout 1s;
proxy_read_timeout 5s;
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 2;
proxy_next_upstream_timeout 3s;
}
}
}验证:
nginx -t
for i in 1 2 3 4 5 6 7 8 9; do
curl -s http://127.0.0.1:8090/
done实验项:
- 记录三节点分布。
- 让8082延迟2秒,观察least_conn和P99。
- 停止8082,观察error log与重试链。
- 恢复8082,观察何时重新获得请求。
- 把算法改为轮询和hash,比较分布。
- 对POST模拟提交后断连,解释为什么不能只靠代理重试。
三十三、商业场景一:扩容消费者式“短暂有效”
接口积压时增加应用实例,短时间延迟下降,随后再次变慢。可能原因:
- 入口放行更多并发,数据库成为新瓶颈。
- 每实例连接池总量叠加,MySQL连接数耗尽。
- 热Key、热点行和锁竞争没有被横向扩容分散。
- 新实例缓存未预热,回源流量上升。
- 重试量随实例数增加。
- 单节点慢请求仍占资源。
与消息队列扩消费者后再次堆积类似,必须比较输入速率、实际处理速率和最慢共享依赖,不能只看实例数。
三十四、商业场景二:支付回调被重试两次
现象:同一个支付回调在两个后端实例都执行,业务产生重复记账尝试。
证据:upstream日志显示第一次节点响应前断连,Nginx随后尝试第二节点;数据库显示第一次可能已提交。
治理:
- 支付流水号唯一约束。
- 幂等状态机。
- 回调原始报文落库。
- 重复回调返回一致结果。
- 代理层不随意为非幂等方法开启重试。
- 对账与补偿。
三十五、商业场景三:NAT导致ip_hash倾斜
现象:办公用户大量请求集中到一台节点,其他节点空闲。
根因:所有用户经过同一企业出口,Nginx看到相同客户端地址,ip_hash得到同一结果。
修复:确认可信代理链和真实IP,但更根本的是去除本地Session依赖,改用无状态Token或共享Session,再选择轮询/least_conn等策略。
三十六、商业场景四:恢复节点反复被打死
现象:节点重启后刚加入流量,CPU、JIT、连接池和缓存尚未稳定,立即接满权重,随后再次超时并被摘除。
治理:
- readiness包含必要初始化。
- 金丝雀小流量。
- 分阶段提高weight。
- 预热JIT、连接池和热点缓存。
- 观察错误率与P99后再放量。
- 使用平台或商业能力实现慢启动。
三十七、面试标准回答
Nginx负载均衡一次请求怎么走
请求命中proxy_pass后,Worker找到upstream,按轮询、least_conn或hash等算法生成候选节点,跳过down和暂时不可用节点,复用空闲连接或新建连接并发送请求;失败时按proxy_next_upstream判断是否记录失败和换节点,同时受次数与总时间限制,最终转发成功响应或返回502/504。
轮询、least_conn和ip_hash怎么选
请求成本接近且服务无状态时用默认加权轮询;请求时长差异大或长连接较多可评估least_conn,但连接少不等于CPU低;ip_hash可提供弱粘滞,但NAT会倾斜、节点变化会重映射,不能代替共享Session和无状态设计。缓存分片可评估consistent hash,但要处理热Key和数据迁移。
max_fails和fail_timeout是主动健康检查吗
不是。开源Nginx通常根据真实代理请求中的连接错误、超时和配置的失败响应做被动判断;达到max_fails后在fail_timeout相关时间内暂时跳过节点,再尝试恢复。
check interval常来自第三方模块,NGINX Plus才有标准商业主动健康能力,不能混用。
Nginx重试为什么可能重复下单
第一个后端可能已经提交数据库,但响应返回Nginx前连接断开;Nginx只看到失败并把POST重试到第二节点,就可能重复执行。代理层无法证明业务是否提交,因此非幂等请求要谨慎重试,订单支付必须有幂等Key、唯一约束、状态机和补偿。
upstream keepalive 32是什么意思
通常表示每个Worker为该upstream缓存最多32条空闲可复用连接,不是总连接上限、并发上限或所有Worker共享32条。活跃连接可以额外存在,多Worker会放大空闲连接,后端FD、keep-alive超时和连接池仍要单独规划。
为什么扩Nginx或应用实例后系统反而更慢
扩容入口和应用会提高进入共享依赖的并发,总数据库连接、慢SQL、热点锁、Redis热Key和重试流量可能放大;瓶颈从应用转移到数据库后,P99和504再次上升。必须按入口、应用、线程池、连接池、DB、Redis和下游做端到端容量分析。
三十八、学习验收
- 画出选节点、连接、失败、重试和返回全过程。
- 用长期比例解释weight,不承诺固定短窗口比例。
- 比较轮询、least_conn、ip_hash和consistent hash边界。
- 说明fair、主动check、least_time和slow_start的开源/商业/第三方边界。
- 制造两次失败,观察max_fails与fail_timeout行为。
- 分别配置哪些失败允许next upstream,并解释总次数和时间限制。
- 设计一个提交成功但响应断开的POST,给出幂等治理。
- 计算多Worker upstream keepalive的连接放大。
- 扩容新节点,观察长连接和Keep-Alive导致的短期倾斜。
- 修改DNS记录,证明目标版本何时更新节点。
- 用upstream日志还原一次“首节点失败、次节点成功”。
- 定位一次NAT导致的ip_hash倾斜。
- 定位一次慢节点拖高整体P99但未被摘除。
- 写出节点恢复后的预热和渐进放量流程。
