NGINX HTTP 负载均衡:上游、算法、会话与动态解析

原文:F5 / NGINX 文档团队,HTTP Load Balancing。本文依据 2026 年 10 月 5 日的完整页面翻译整理。© 2026 F5, Inc.,原站保留所有权利,NGINX 为 F5 注册商标;源页没有为这篇文档单独列出开放许可,不能把软件许可自动套到文章上。

把 HTTP 请求分配给多个应用实例,可以改善资源使用、吞吐量、延迟与故障容忍能力。NGINX 与 NGINX Plus 都能执行 HTTP/HTTPS 的七层负载均衡;TCP、UDP 等四层场景则应另看 TCP and UDP Load Balancing。负载均衡不会自动使应用变成无状态系统,认证会话、共享数据和失败重试仍要与应用一起设计。

请求先进入 NGINX,再按算法与权重分发到正常上游;备用服务器仅在主服务器不可用时接收请求。
未完纪原创技术示意图;用于说明流程,不是运行截图。

版本核查说明:原教程部分段落继续按旧产品边界描述 Plus 功能。本文保留所有技术主题及原配置,并依据同日 upstream 指令参考校订:max_conns 从 1.11.5 起不再仅限商业订阅;random 在 1.15.1 出现,基本随机及 two/least_conn 不应一概标成 Plus;动态 DNS 的 resolve 从 1.27.3 起开放;sticky 从 1.29.6 起不再整体限于商业订阅(sync 仍有商业限制);独立的 least_time 从 1.31.0 起开放。发行版可能仍打包旧版本,必须按实际二进制及模块核对。slow_start、queue、ntlm、主动健康检查及 Plus API 等仍有相应商业/模块边界。

把请求代理到一组服务器

在 http 上下文中用 upstream 定义服务器组。组内的 server 指令表示一个上游地址,不要与定义虚拟主机的 server {} 块混淆。域名可能解析到多个地址,因此三条配置不一定只产生三个实际后端。

http {
    upstream backend {
        server backend1.example.com weight=5;
        server backend2.example.com;
        server 192.0.0.1 backup;
    }
}

这里定义了名为 backend 的上游组。weight=5 是权重,backup 标记备用服务器。把组名交给 proxy_pass,即可让虚拟主机将请求转给该组:

server {
    location / {
        proxy_pass http://backend;
    }
}

原文把两段组合后的基础配置如下,普通节点各使用默认权重,第三个节点备用:

http {
    upstream backend {
        server backend1.example.com;
        server backend2.example.com;
        server 192.0.0.1 backup;
    }

    server {
        location / {
            proxy_pass http://backend;
        }
    }
}

其他应用协议应使用自己的 pass 指令,例如 fastcgi_pass、memcached_pass、scgi_pass 或 uwsgi_pass。这些示例是 http 配置片段,不包含完整主配置所需的其他上下文。域名、地址和路径都必须改为自己的实验环境;原文的 192.0.0.1 不能被当成已经可用的后端。

选择负载均衡算法

除默认轮询外,应把算法指令放在 upstream 的 server 列表前面。选择依据是连接时长、请求成本、缓存键和会话要求,不能只看一个“更高级”的名字。

轮询:默认按权重轮流分发

不写算法指令就是加权轮询。权重相同的节点按轮询方式分配请求。

upstream backend {
   # no load balancing method is specified for Round Robin
   server backend1.example.com;
   server backend2.example.com;
}

最少连接:比较活跃连接数

least_conn 将请求发给活跃连接较少的服务器,同时考虑服务器权重。当请求的处理时间差异较大时,连接数可能比单纯请求计数更有参考价值;相同最少连接数的节点还会按加权轮询选择。

upstream backend {
    least_conn;
    server backend1.example.com;
    server backend2.example.com;
}

IP Hash:依据客户端地址选择后端

ip_hash 使用 IPv4 地址的前三个字节,或完整 IPv6 地址计算哈希。相同地址通常落到同一服务器,除非该服务器不可用。这不等同于用户身份:多个用户可能共享出口 IP,用户 IP 也可能变化。

upstream backend {
    ip_hash;
    server backend1.example.com;
    server backend2.example.com;
}

想临时移除一个节点而保留哈希分布结构,可以标记 down。原文展示以下节点清单:

upstream backend {
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com down;
}

编者注:这段本身没有写 ip_hash;,不能仅靠 down 就获得 IP Hash 行为。若目的是前一段的固定映射,应保留前一段算法指令,再给目标节点加 down。在该节点恢复前,原本分配到它的请求会改投可用节点。

通用哈希:按自定义键选择后端

hash 可以采用字符串、变量或两者的组合,例如来源地址和端口,也可以像下面这样使用请求 URI:

upstream backend {
    hash $request_uri consistent;
    server backend1.example.com;
    server backend2.example.com;
}

consistent 启用 ketama 一致性哈希。增删节点时只需重映射部分键,能减少缓存未命中,适合缓存服务或会积累状态的应用。它减少的是重映射范围,不意味着会自动复制状态或保证任意故障下不丢失会话。

最短时间:结合响应延迟和连接数

least_time 同时参考平均响应时间与活跃连接数,并考虑权重。header 表示接收响应头的时间,last_byte 表示完整响应时间,inflight 还把尚未完成的请求纳入计算。原页把 header 简写成“首字节”,此处按指令参考使用更准确的“响应头”。

upstream backend {
    least_time header;
    server backend1.example.com;
    server backend2.example.com;
}

该算法在 NGINX Plus Release 6 起支持,开源 NGINX 从 1.31.0 起支持;更老的开源发行版不能直接照抄。

随机选择与随机抽取两个节点

random 按权重随机选一个节点。加 two 则先随机挑两个,再用指定方法决定其中一个:least_conn 比较活跃连接数,least_time=header 比较响应头时间,least_time=last_byte 比较完整响应时间。

upstream backend {
    random two least_time=last_byte;
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com;
    server backend4.example.com;
}

上例使用随机二选一及完整响应时间。版本区别:基本 random 与 two/least_conn 不应被误写为全都只限 Plus;但核对日指令参考仍将 random 中的 least_time 选择方法列为商业能力,不能因为独立 least_time 已开放就推断这个组合也开放。随机二选一适合多台负载均衡器共享同一批后端、各自无法掌握所有请求的环境;单个负载均衡器具有完整观测时可考虑其他策略。

按容量设置权重与备用节点

weight 未指定时为 1。轮询、最少连接和随机等算法都能使用权重。

upstream backend {
    server backend1.example.com weight=5;
    server backend2.example.com;
    server 192.0.0.1 backup;
}

两个主节点权重为 5:1,因此在健康节点、均匀调度等前提下,可以用“每 6 个请求中约 5 个到第一个、1 个到第二个”理解分配比例。backup 不参与正常分配,两个主节点都不可用时才接收请求。不要把这个教学比例当成各种并发、失败、重试和不同算法下逐个请求都严格成立的保证。backup 不能与 hash、ip_hash 或 random 随意组合,指令参考明确列有这种限制。

让恢复的服务器缓慢加入

刚恢复的节点如果马上承受满负荷,可能超时并再次被判失败。Plus 的 slow_start 将有效权重从 0 逐渐恢复到配置权重:

upstream backend {
    server backend1.example.com slow_start=30s;
    server backend2.example.com;
    server 192.0.0.1 backup;
}

30s 表示恢复过程持续 30 秒;默认值 0 禁用慢启动。组中只有一个服务器时,max_fails、fail_timeout 和 slow_start 被忽略,该服务器不会因这些参数被视为不可用。慢启动也有与哈希、随机算法的组合限制,部署前要查对应指令版本。

会话保持:让一段会话回到同一后端

会话保持通过标识会话,把后续请求交给先前选择的服务器。原教程按 Plus 介绍 sticky 的三种方法;在当前参考中 sticky 已从 1.29.6 起开放,但旧版开源 NGINX 仍不能直接使用。旧环境可使用 hash 或 ip_hash 获得相应的分配稳定性,但其语义并不完全等同于完整的会话保持。

Sticky cookie

NGINX 在首次响应中加入用于表示所选服务器的 cookie,后续请求带回 cookie,就可回到对应服务器。

upstream backend {
    server backend1.example.com;
    server backend2.example.com;
    sticky cookie srv_id expires=1h domain=.example.com path=/;
}

srv_id 是 cookie 名;expires=1h 表示浏览器保存一小时;domain 和 path 限定作用范围。这个路由 cookie 不是用户认证凭据,不能用“命中了同一个后端”代替登录校验。原例使用宽泛的父域,且未指定 Secure、HttpOnly、SameSite;实际 HTTPS 部署应根据兼容性和业务范围设置这些属性,尽量缩小域作用范围。

Sticky route

每个后端用 route 标记路线。应用在 cookie 或 URI 中携带对应标识;配置依次查看列出的变量,使用第一个非空值进行路由匹配。

upstream backend {
    server backend1.example.com route=a;
    server backend2.example.com route=b;
    sticky route $route_cookie $route_uri;
}

原文先查 $route_cookie,再查 $route_uri。配置完整性:这两个变量不是自动定义的;还要用应用实际的 cookie/URI 规则通过 map 等方式提取,才能构成可加载的配置。不能直接把这一个块视为完整配置,也不应将敏感会话令牌随意放进容易被日志记录的 URL。

Sticky learn

NGINX 从请求和响应中学习“会话标识 → 后端”的对应关系。示例中应用返回 EXAMPLECOOKIE,代理从响应头变量建立映射,再从请求 cookie 查找:

upstream backend {
   server backend1.example.com;
   server backend2.example.com;
   sticky learn
       create=$upstream_cookie_examplecookie
       lookup=$cookie_examplecookie
       zone=client_sessions:1m
       timeout=1h;
}

create 指定新会话标识从哪里取;lookup 指定后续请求的标识来源;zone=client_sessions:1m 声明一块 1 MiB 共享区保存映射;timeout=1h 表示会话映射的空闲超时。

编者校订:原文说这种方法“不需要在客户端保存任何 cookie”,容易产生误解。更准确的是不必再为负载均衡新增一个专用 cookie;这段示例仍依赖应用的会话 cookie。保存在服务器端的是路由映射,不是说客户端无需发送会话标识。

多台 NGINX 需要同步映射时,相关共享区必须同名,每台都配置 zone_sync,并增加 sync 参数。原文给出以下局部片段,其中末尾右花括号属于外围 upstream:

sticky learn
       create=$upstream_cookie_examplecookie
       lookup=$cookie_examplecookie
       zone=client_sessions:1m
       timeout=1h
       sync;
}

这不是独立完整配置。sync 仍有商业订阅边界,集群同步还必须限制网络访问。详情见原文引用的 Runtime State Sharing in a Cluster。

限制连接,并决定超额请求如何处理

max_conns 控制到某个上游的同时活跃连接数,默认 0 表示不限制。queue 用于在暂时选不到节点时排队,设置最大排队请求数和等待超时:

upstream backend {
    server backend1.example.com max_conns=3;
    server backend2.example.com;
    queue 100 timeout=70;
}

当无法立即选择服务器时请求进入队列;队列满或等待超时将得到错误,指令参考明确为 502。没有 queue 时,也不能假定请求会自动无限等待。max_conns 本身在现代开源版可用,而这个 queue 示例属于商业能力。

编者校订:原文把 keepalive 情况简写为“忽略限制”。准确说法是 max_conns 限制活跃连接;多 worker 和空闲 keepalive 并存时,活跃加空闲的总连接数仍可能大于此值。若没有共享 zone,限制按每个 worker 分别计算,不能把它当成全实例严格总上限。

健康检查与多个 worker 的共享状态

NGINX 可以根据请求失败暂时避开节点,Plus 还可主动周期探测并让恢复节点重入。被动失败判断、主动探测和应用自身的“就绪”状态并非同一个概念。具体配置见 HTTP Health Checks;原文这里只引用这一主题,没有给出完整探测配置。

如果 upstream 没有 zone,各 worker 各自保留服务器组状态、连接数以及失败计数。某个 worker 认为节点失败,其他 worker 可能尚未观察到同样的失败,继续发请求过去。加入 zone 后,相关配置和计数放进共享内存,多个 worker 才能依据同一份数据作决定。主动健康检查与动态上游管理需要相应共享状态,但仅添加 zone 并不会自动获得 Plus API 或主动检查模块。

原文用 max_fails × worker 数量 说明未共享情况下可能需要更多失败才能被各 worker 识别。这个乘法是解释直觉,不是所有流量分布下恰好如此的全局阈值;每个 worker 仍独立处理自己的 fail_timeout 时间窗。低负载时 least_conn 也会受独立计数影响:两个 worker 可能都认为同一个节点最空闲。高负载会减轻这种偏差,但共享 zone 更直接地解决状态不一致。

共享区该分配多大

没有适合所有环境的固定数值。所需空间取决于会话保持、健康检查、DNS 重解析以及服务器标识方式。原文给出的例子是使用 sticky route 和一次健康检查时,256 KiB 可容纳约 128 个 IP:port 节点,或 88 个解析到单个 IP 的 hostname:port 节点,或 12 个会解析到多个 IP 的 hostname:port 节点。这是特定条件的容量示例,不是内存精确预算或性能保证。

借助 DNS 在运行中更新后端

在 server 上加 resolve,并配置可信 DNS resolver,就能跟踪域名对应地址的变化,无须为每次地址变化重启代理。服务器组必须使用共享内存。

http {
    resolver 10.0.0.1 valid=300s ipv6=off;
    resolver_timeout 10s;
    server {
        location / {
            proxy_pass http://backend;
        }
    }
    upstream backend {
        zone backend 32k;
        least_conn;
        # ...
        server backend1.example.com resolve;
        server backend2.example.com resolve;
    }
}

resolver 10.0.0.1 指定 DNS 服务器;默认缓存尊重 DNS TTL,valid=300s 改为五分钟;resolver_timeout 10s 约束解析等待;ipv6=off 只使用 IPv4,默认可解析双栈。域名解析到多个 IP 时,这些地址都进入上游组,随后按 least_conn 分配请求。记录变化被解析到后,上游列表随之更新。

原文将这项能力写为 Plus 专属,本文已补充 1.27.3 的开源版本边界。resolver 应位于受信任和受保护的网络,防止 DNS 欺骗;10.0.0.1 只是原文环境地址,不能假定你的网络也有这一服务。

Microsoft Exchange 与 NTLM 连接保持

NGINX Plus Release 7 及以后可以对 Exchange 流量做代理和负载均衡。原文按以下顺序搭建:先在 location 代理到 https://exchange:

location / {
    proxy_pass https://exchange;
    # ...
}

然后保持 HTTP/1.1 上游连接,并清空 Connection 头:

location / {
    # ...
    proxy_http_version 1.1;
    proxy_set_header   Connection "";
    # ...
}

在同名 upstream 中开启 ntlm:

http {
    # ...
    upstream exchange {
        zone exchange 64k;
        ntlm;
        # ...
    }
}

再加入实际 Exchange 节点,并按需要选择负载均衡算法;如果使用非轮询算法,应先声明算法,再声明 ntlm:

http {
    # ...
    upstream exchange {
        zone exchange 64k;
        ntlm;
        server exchange1.example.com;
        server exchange2.example.com;
        # ...
    }
}

下面是源文的完整 NTLM 示例:

http {
    # ...
    upstream exchange {
        zone exchange 64k;
        ntlm;
        server exchange1.example.com;
        server exchange2.example.com;
    }

    server {
        listen              443 ssl;
        ssl_certificate     /etc/nginx/ssl/company.com.crt;
        ssl_certificate_key /etc/nginx/ssl/company.com.key;
        ssl_protocols       TLSv1.2 TLSv1.3;

        location / {
            proxy_pass         https://exchange;
            proxy_http_version 1.1;
            proxy_set_header   Connection "";
        }
    }
}

部署风险:这仍是教学配置。上游节点没有写端口时默认是 80;后端实际提供 TLS 的端口可能是 443,应明确配置,不能因为 proxy_pass 写了 https 就假定 server 地址自动使用正确服务端口。入口 TLS 证书路径也是示意。更重要的是,proxy_pass https://... 表示加密传输,不等于自动验证上游证书;应核对 proxy_ssl_verify、受信 CA、SNI 与证书名称。

例如,以下是与原文有意不同的安全配置片段:明确上游 TLS 端口并启用证书验证。它假定两台后端都持有对 exchange.example.com 有效的证书,实际名称与 CA 文件必须替换为真实受控配置;这段没有包含入口证书和完整虚拟主机设置,也未测试。

upstream exchange {
    zone exchange 64k;
    ntlm;
    server exchange1.example.com:443;
    server exchange2.example.com:443;
}

# 放在对应 server 块内
location / {
    proxy_pass https://exchange;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_ssl_verify on;
    proxy_ssl_trusted_certificate /etc/nginx/trust/upstream-ca.pem;
    proxy_ssl_server_name on;
    proxy_ssl_name exchange.example.com;
}

NTLM 需要将上游连接与客户端连接关联起来,以保留认证上下文;它仍是商业功能。新版本的连接默认值可能已经变化,保留显式 HTTP/1.1 设置有利于说明原教程意图,但不能省略对目标版本的核查。进一步部署细节见原文的 Exchange 专项指南。

用 Plus API 动态管理上游

NGINX Plus API 可查看所有节点或单个节点、修改参数,以及增删服务器。这里只介绍能力,具体步骤见 Configuring Dynamic Load Balancing with the NGINX Plus API。管理接口必须限制到受控访问范围并设置认证/授权,不能因为它属于负载均衡配置就暴露给公网。

本文未加载配置、未重启 NGINX、未发流量、未压测或连接任何 Exchange 服务。示例中的响应比例、故障切换和恢复过程都是原理及预期行为,实际部署仍需在隔离环境核对语法、版本、TLS、超时、重试及会话语义。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容