nginx 1.29.6 新特性解读:官方粘性会话与性能优化实践
nginx 1.29.6 发布了作为主线版本这算得上是今年值得仔细看的一次更新。我在生产环境里维护着一批 nginx 网关从 1.16 一路追到 1.28每次主线版本发版我都会先翻 changelog再拿测试机验证确认没有坑之后才考虑要不要跟。这次 1.29.6 最让我关注的有两件事一是 upstream 终于支持原生粘性会话二是性能与稳定性方向有一批改进。这两个点对搞负载均衡、多节点部署的朋友来说属于能直接改变架构选型的内容。粘性会话这个词搞过后端的人应该不陌生。应用多节点部署之后用户的登录状态、购物车数据、临时上传进度都存在某台服务器上如果负载均衡把请求随机分到不同节点轻则用户反复掉线重则直接把业务打崩。以前 nginx 官方不支持这个能力大家只能用第三方模块或者用 ip_hash、hash 指令硬凑。现在 1.29.6 官方主线直接支持意味着以后在标准环境里就能做会话保持不用再为了一个 sticky 模块去编译维护一堆第三方代码了。这篇文章我会先把这次更新的核心内容拆开讲清楚然后重点聊粘性会话的原理和配置再补上性能调优和升级后的常见坑。适合正在用 nginx 做反向代理和负载均衡的运维、后端开发以及准备升级主线版本但心里没底的团队参考。1. 1.29.6 的核心看点官方粘性会话为何等了这么久1.1 为什么粘性会话此前一直靠第三方模块nginx 官方在很长一段时间里反代负载均衡只有轮询、权重、ip_hash、最少连接这几种策略。这些策略的共同点是“负载均衡优先”每个请求尽量均匀分发给后端不关心请求来自哪个用户、和之前的请求有没有关联。这在纯 API 网关场景没问题但一旦涉及 Session、Cookie、长连接就很容易捅娄子。社区很早就有解决方案。老牌的 nginx-sticky-module-ng很多人编译过稍微新一点的做法是用 hash 指令自己按 Cookie 或 Header 做一致性哈希。但这些方案都有比较明显的毛病。第三方模块要跟随 nginx 版本重新编译每次 nginx 升级模块编译失败的情况我都遇到过不止一次。hash 方案呢能用但对节点增删不友好调整后端的瞬间会有大量请求漂移到其他节点Session 直接丢失。这背后其实有 nginx 官方的取舍。官方对核心模块的稳定性要求极高一个新特性要进主线得先解决权限模型、模块加载机制、与健康检查的协作方式等一系列问题。粘性会话不像轮询那样无状态它天然引入“用户和后端节点绑定”的概念这会和故障转移产生冲突——如果用户绑定的那台后端挂了请求到底该转发到哪里这个问题处理不好官方宁可不做。所以这次 1.29.6 上线粘性会话从产品决策到协议设计都花了不少心思。1.2 新增粘性会话后哪些场景直接受益第一类明显受益的场景是带登录态的传统 Web 应用。比如企业内部系统、CMS、电商网站用户登录之后 Session 存在本机内存里Session 没有做共享这种架构下粘性会话几乎是刚需。以前没有官方支持要么在应用层做 Session 复制要么引入 Redis 共享 Session要么就忍着用户随机掉线。现在只需要在 nginx 层做一次配置把同一个用户的请求固定到同一台后端节点应用层基本不用动。第二类是 WebSocket 和长连接场景。nginx 代理 WebSocket 已经非常成熟但长连接比普通 HTTP 请求更需要会话保持。一个 WebSocket 连接建立之后后端的进程和前端是保持双向通信的一旦连接被转发到另一台节点这条链路就会断裂。比如用 nginx 代理 FreeSWITCH 的 WS 端口、IM 服务的推送通道这类业务天然需要粘性会话。第三类是文件上传下载、分片传输这类有状态流。大文件分片上传每一片都希望落到同一台后端否则合并的时候找谁要数据粘性会话能把同一用户的流式请求稳稳固定在一台节点上省掉很多应用层的合并逻辑。1.3 性能与稳定性提升的实际价值其实每次主线版本更新changelog 里都会有一批性能优化和稳定性修复。这种更新通常不会大张旗鼓宣传但对生产环境的影响非常直接。从我追踪版本的经验来看这类改动主要集中在这几个方向。一是连接生命周期相关的问题。包括连接复用、空闲连接回收、异常连接释放等。生产环境中经常出现的 TIME_WAIT 堆积、worker 连接数缓慢上涨、长时间运行后连接池膨胀基本都是这类问题引起的。新版本往往会在 upstream 连接的复用策略和等待队列上做调整让整个并发连接模型更平滑。二是 HTTP/2 和 HTTP/3 的实现完善。HTTP/2 已经普及但多路复用下的流量控制问题、连接并发导致的性能回退问题不同版本的表现差异很大。HTTP/3 也就是 QUIC 协议nginx 一直在推进每一次主线更新都会带上 QUIC 相关的修复和改进。如果你打算开启 HTTP/3这类底层的协议栈完善对稳定性帮助很大。三是内存使用和 worker 进程的稳定性。nginx 对内存的控制一直很严格但应对极端流量、大量非法请求、畸形报文的时候偶发的高内存占用和 worker 崩溃也没有完全绝迹。主线版本通常会在这些边缘场景上打补丁所以“稳定性全面提升”这种说法其实是落到这些具体场景里的。2. 粘性会话的原理与选型从 Cookie 到一致性哈希2.1 粘性会话解决的核心矛盾负载均衡的核心逻辑是把流量分散开让每台后端都能吃饱但又不能撑死。问题来了请求可以随便分用户的数据状态却不能随便分。用户登录之后Session 存在 A 节点下一次请求如果被负载均衡分到 B 节点B 节点查不到这个 Session就只能让用户重新登录。粘性会话的思路很简单在负载均衡层加一个“绑定关系”让同一个用户的请求尽量落到同一台后端。这与 Session 共享是两种不同的解决路径。Session 共享是让所有节点都能访问同一份状态属于“数据搬家”粘性会话则是把用户绑死在固定的节点上属于“人不搬家”。两种方式没有绝对的好坏但粘性会话在改动成本上更低尤其适合改造老系统。只要 nginx 层配置一下业务代码完全不用动不香吗2.2 Cookie 粘性如何工作Cookie 粘性是实现粘性会话最常见的方式。流程大概是这样的用户第一次访问某个服务nginx 所在的负载均衡层发现这个请求没有携带粘性 Cookie就按照当前策略选择一台后端节点接收请求并在响应里种一个 Cookie比如 srv_idbackend-a。用户接下来的请求都会带着这个 Cookienginx 看到 Cookie 里的值之后直接把请求转发到 backend-a完成绑定。这里有个细节需要理解nginx 并不需要真的去后端存储里确认这个 Cookie 是否有效。它维护的是一个映射关系即“某个 Cookie 值对应哪个后端节点”。这个映射可以放在共享内存里也可以把后端节点信息直接编码在种下去的 Cookie 值中后者避免了大量连接状态占用内存。生产环境里更推荐后一种方式即使 nginx 重启、worker 重新调度Cookie 值仍然有效不影响粘性。还有一类是基于一致性哈希的实现。不种 Cookie而是根据请求里的某个稳定标识比如用户 ID、来源 IP、特定的 Header做哈希计算算出来的结果就是后端节点的编号。这种方式的优点是没有额外的 Cookie也不依赖浏览器是否支持 Cookie缺点是如果后端节点数量变化哈希结果会大面积漂移。2.3 几种粘性策略怎么选不同策略的选型主要看你的业务形态和现有基础设施我整理了一个对比表格策略实现方式优点缺点适用场景Cookie 粘性种 Cookie 并映射到节点精确、用户经验好、不受代理 IP 影响依赖浏览器 Cookie无 Cookie 客户端不适用带登录态的 Web 应用、购物车、企业系统ip_hash按来源 IP 哈希无需额外配置天然支持多出口 IP 会把用户全塞到一台节点出口 IP 单一的内网服务、IPv6 场景Hash Key按 Header/参数/指定 key 哈希灵活可针对业务标识进行哈希节点变更时哈希漂移Session 容易丢无 Cookie 的 API 网关、需要按租户隔离的场景第三方 sticky 模块编译第三方模块功能成熟可自定义升级跟随成本高模块质量参差不齐旧环境暂时无法升级到新版本我在实际项目里给电商类站点用的是 Cookie 粘性种一个短期 Cookie 就好给内部 API 网关用的是基于 Header 的哈希比如按 X-User-Id 哈希不用管 Cookie也不会因为浏览器禁用 Cookie 而失效。这个选择逻辑很简单有明确用户身份的优先用 Cookie 粘性按来源 IP 和入口网关做聚合的就用 ip_hash 或 Hash Key。3. 升级 1.29.6 与粘性会话配置实操3.1 升级前的检查与备份升级之前我习惯先做三件事。第一明确当前版本。命令是nginx -v如果发现当前是某个很老的稳定版跳版本升级要格外注意配置兼容性。第二完整备份配置。不仅仅是复制一份 nginx.conf而是把 conf.d 下的所有 conf 文件、证书目录、还有编译时的模块列表全部记录下来。第三确认回滚路径。git 里留一份当前可用的 nginx 二进制和配置万一新版本有兼容性问题能在一分钟内回到旧版本。还有一点容易被忽略就是确认第三方模块兼容性。如果你当前环境编译过第三方模块升级到 1.29.6 之后这些模块大概率要重新编译。有些模块原作者已经不怎么维护了在新版本上编译可能直接报错。建议升级前先在新版本源码上跑一次./configure加上原有参数看看模块能不能编译通过。这一步卡住的话就别急着上生产先解决模块兼容性问题。3.2 平滑升级的完整流程如果你用的是官方源码编译方式平滑升级流程相对成熟。先把 1.29.6 的源码包下载下来解压沿用之前的编译参数重新 configure 并编译新二进制然后用新二进制替换旧二进制。保险起见替换前可以把旧二进制备份为 nginx.old。替换完成后执行kill -USR2 旧master进程PID让新老版本同时运行再kill -WINCH 旧master进程PID优雅关闭旧 worker最后kill -QUIT 旧master进程PID退出旧的 master。用系统自带软件源安装的 nginx升级方式会更简单直接执行包管理器的升级命令就行。但要注意RedHat 系和 Debian 系默认的 nginx 版本往往比主线版落后不少。如果你等不到官方源更新可以自行添加 nginx 官方源或者直接使用编译安装的方式。我这里更推荐编译安装因为可以精确控制编译参数还能把第三方模块一起编进去。升级完成之后我的验证顺序是nginx -t校验配置nginx -V确认版本和编译参数然后 reload 一次观察 error.log 有没有新增告警再通过压测或者线上小流量验证基础功能。3.3 配置一个带粘性会话的 upstream假设我们有两个后端节点部署了同一个 Java Web 应用用户登录状态存在本机 Session。配置粘性会话之后同一用户的请求会稳定落到同一台节点。生产环境下的配置大致长这样upstream app_backend { zone app_backend 64k; sticky cookie srv_id expires1h maxage1h; server 192.168.1.10:8080 max_fails2 fail_timeout10s; server 192.168.1.11:8080 max_fails2 fail_timeout10s; } server { listen 80; server_name app.example.com; location / { proxy_pass http://app_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }这里有一个我踩过的坑升级到 1.29.6 之后如果配置sticky指令报 unknown directive说明你使用的这个版本可能还没编译粘性会话模块或者指令名有细微差异。毕竟 nginx 不同版本的模块名和参数会有调整保险做法是先看当前版本的官方文档确认模块名再按文档语法来配置。我在测试机上试过第一次配置时就是因为指令名写错导致 nginx -t 直接报错。另一个关键参数是expires。Cookie 粘性会给客户端种一个 Cookie这个 Cookie 的过期时间要设计好。太短的话用户没过多久就失效还得重新绑定太长的话后端节点要下线维护时用户可能还挂在旧节点上请求频繁失败。我的经验是业务 Session 持续多久Cookie 就设置多久最多不要超过一天。电商场景设置 30 分钟到 1 小时比较合适管理后台这类长会话可以设置 8 小时。3.4 验证粘性是否生效的两种方法配置完粘性会话怎么确认是真的生效了我常用的方法是先看 Set-Cookie 响应头再用带 Cookie 的请求做连续性验证。第一步不带 Cookie 请求一次登录接口打开抓包工具或者直接用 curlcurl -sI http://app.example.com/login | grep -i set-cookie正常响应里应该能看到 nginx 下发的 Cookie比如srv_idbackend-a。如果看到的是业务自己种的其他 Cookie没有 srv_id说明粘性模块可能没生效或者被业务层覆盖了。第二步把第一步拿到的 Cookie 值原样带回去连续请求几次业务接口curl -s -b srv_idbackend-a http://app.example.com/api/user/info curl -s -b srv_idbackend-a http://app.example.com/api/order/list这时候去后端的 access log 里查请求来源 IP 或者响应时间两个请求应该都落在 backend-a 节点上。如果第二次请求落到了 backend-b说明粘性配置有问题或者 Cookie 在浏览器侧被清了。多节点场景最好给每个后端节点配置独立的 access_log方便做这种验证。4. 性能与稳定性调优的落地姿势4.1 长连接与 keepalive 的正确参数nginx 1.29.6 这类主线版本在 upstream 长连接处理上通常会有改进但你的配置如果一直没调再好的改动也发挥不出来。之前排查过一次线上问题后端 Tomcat 的连接数经常被打满后来发现 nginx 每次请求都重新建连没有启用 upstream keepalive。补上之后连接数和后端负载同时降了不少。upstream app_backend { zone app_backend 64k; sticky cookie srv_id expires1h; server 192.168.1.10:8080; server 192.168.1.11:8080; keepalive 32; } server { listen 80; location / { proxy_pass http://app_backend; proxy_http_version 1.1; proxy_set_header Connection ; } }这里有两个容易忽略的细节。第一必须设置proxy_http_version 1.1否则 nginx 到后端默认走 HTTP/1.0连接无法复用。第二proxy_set_header Connection 必须清空不然头信息里留着 Connection: closekeepalive 照样不生效。这两个点少一个keepalive 就等于没配。keepalive 参数也不是越大越好。32 条空闲连接看后端能承受多少。后端 Tomcat 并发线程通常就 200 左右你在一台 nginx 上 keepalive 设置 100意味着每台后端要维护 100 条空闲连接后端连接池的负担会很大。合理的设置是先看后端连接池上限再给 nginx 配一个合适的值一般 16 到 64 之间比较稳妥。4.2 HTTP/3 要不要现在就上1.29.6 主线版本里 HTTP/3 和 QUIC 的支持又往前走了一步很多朋友已经开始纠结要不要开启。我的判断是HTTP/3 确实能提升弱网环境下的用户体验尤其针对移动端和跨地域用户但生产环境直接全量上线安全隐患和兼容性问题都不少。最常见的坑是中间设备拦截。运营商网络或者企业防火墙里有些老设备不认识 QUIC 报文直接把 UDP 443 丢掉导致浏览器报net::err_quic_protocol_error。这个问题在办公网、校园网尤其明显。另一个坑是监控体系。HTTP/3 的请求在抓包工具和 Web 日志里的形态和 TCP 完全不一样监控、日志分析都要跟着调整如果监控没跟上出了问题定位会很痛苦。我建议的做法是渐进式开启。先在某个测试域名或者对部分用户开启 HTTP/3观察访问成功率、报错率、页面加载时间跑一段时间稳定之后再逐步扩大流量。如果业务对网络稳定性要求极高暂时不开也不丢人HTTP/2 TLS1.3 在绝大多数场景下已经够用了。4.3 SSL 握手性能优化清单升级到新版本之后SSL/TLS 相关的优化也值得重新过一遍。nginx 默认配置在手写环境下往往没有展示出真正的性能几个改动能明显减少握手时间和 CPU 占用。第一个是ssl_session_cache。这个参数非常关键它让 nginx 在本地缓存 TLS 会话票据后续客户端重新建连可以直接复用省掉一次完整的 TLS 握手。生产环境建议设置为shared:SSL:10m10MB 共享内存大约能存 8 万多个会话。如果你发现客户端频繁握手、页面加载偏慢多半是这个没设好。第二个是ssl_protocols和ssl_ciphers。建议只开启 TLSv1.2 和 TLSv1.3把 SSLv3、TLSv1.0、TLSv1.1 全部关闭。TLSv1.3 在握手轮次和加密效率上比老版本强太多只要客户端兼容优先开启。这个操作放在 nginx 配置里只是一个指令但对安全合规和性能都有实际提升。第三个是证书链优化。证书链里有中间证书和根证书nginx 每次握手要发送整条证书链。证书链太长、证书文件过大握手时间就会增加。把证书文件整理成 PEM 格式并检查证书链完整性虽然不起眼但访问速度会有体感提升。5. 升级后常见问题与排查实录5.1 排查工具与应急思路升级过程中不管做多少准备上线后还是可能出问题。我的排查思路一般都是自底向上先看 error.log再看 access.log最后才动具体请求验证。查看 error.log 时建议带上时间线定位到一个比较长的时间窗口用tail -f或者grep筛选关键字。常见的错误信息格式大概是[error]、[crit]、[emerg]等严重级别从低到高。[emerg]出现意味着配置错误nginx 起不来[crit]出现在磁盘写满、内存不足等系统级问题[error]则多是业务请求相关的错误。看到报错先别急着百度先认真读一遍日志内容大多数情况下报错信息已经很明确了。排查连接问题时nginx -T非常有用。它可以导出 nginx 加载后的完整配置比直接看散落的配置文件更清楚能看清sticky、keepalive、proxy_pass这些指令最终生效的值避免因为 include 引入多个配置文件时出现覆盖和冲突。配合curl -v看请求头能直接确认 Cookie、响应头是否符合预期。5.2 高频问题速查表问题可能原因解决办法配置 sticky 后 nginx -t 报 unknown directive粘性模块未编译或模块名写错确认当前版本是否包含对应模块查阅官方文档修正指令名粘性会话不生效用户反复掉线浏览器禁用了 Cookie、多个域名间 Cookie 未共享、代理层多次转发检查 Set-Cookie 路径和域名用 curl 模拟带 Cookie 请求验证后端节点添加或删除后大量 Session 丢失节点变更导致哈希映射漂移旧用户被重定向到新节点规划好维护窗口结合一致性哈希的前缀保留策略逐步调整升级后部分接口 403/404配置文件不兼容server_name、location 匹配规则有变化grep 对比新旧配置文件差异重点检查 location 和 rewrite 规则开启 HTTP/3 后浏览器访问报 err_quic_protocol_error中间网络设备拦截 UDP 443 报文暂时关闭 HTTP/3或限制到测试域名小流量验证高并发下 error.log 出现 worker_connections are not enoughworker_connections 设置过小根据系统 ulimit 调大 worker_connections配合调整 worker_processesreload 后连接瞬间断掉worker 进程重启长连接被回收业务侧实现重连机制避免在流量高峰时 reload5.3 几个来自实践的避坑经验第一升级前一定要在一台独立测试机跑一遍全量回归。我在一次升级中吃过亏新版本配置校验通过但旧配置里的某个第三方指令在新版本中行为变化了上线后即时接口出现了路径漂移。回归用例要覆盖至少一遍登录、上传、WebSocket 连接等核心场景不能只压测不验功能。第二粘性会话的 Cookie 名称不要和业务 Cookie 冲突。业务本身可能种了一个名为 srv_id 的 Cookienginx 又种了一个同名 Cookie后种的值会覆盖前种的值导致粘性失效。我在一次配置时把粘性 Cookie 命名为 app_srv_id从源头上避免冲突。第三升级后不要立即清空旧版本二进制。保留 nginx.old保留旧配置备份至少保留到新版本稳定运行一周以上再清理。真出了兼容性问题回滚路径远比修复问题更可靠。6. 从 1.29.6 看主线版本选型与演进6.1 主线版和稳定版怎么选nginx 的版本发布节奏里主线版本和稳定版本是并行的。主线版本更新频繁新功能都在主线里先出现但并不意味着不稳定只是更新迭代速度快。稳定版本更保守只做严重安全修复和 bug 修复不引入新功能。很多团队对主线版本有心理顾虑觉得“稳定版才适合生产”这个观念在早期确实是主流但现在 nginx 官方对主线版本的定位已经非常成熟生产环境用主线版本的团队越来越多。如果你所在团队对新技术接受度较高且有一定的测试能力我建议直接跟随主线版本。粘性会话、HTTP/3 改进这些新东西只有主线版本才有稳定版要等很久才会合入。如果你所在的行业对变更极度敏感或者你只有一台核心网关、没有测试环境那还是保守一点等稳定版发布之后验证没问题再升级也不迟。我的个人习惯是测试环境永远用最新主线版本生产环境比主线版本滞后一个版本等社区反馈稳定之后再跟进。这样既不会错过新特性又能用社区的问题反馈作为避坑依据。6.2 nginx 在网关生态里的位置从 1.29.6 这次的更新其实能看到 nginx 的演进方向官方对网关层能力越来越重视。粘性会话过去是商业版 nginx Plus 的卖点之一现在进了主线说明官方在把一些高频需求逐步下沉到开源版本里。这对开发者来说肯定是个好消息。但也要清醒看到nginx 核心的定位始终是高性能反向代理和 Web 服务器它不会变成一个功能冗杂的应用网关。像灰度发布、熔断限流、服务发现集成、自定义鉴权这类能力纯 nginx 配置会越写越复杂维护成本很高。如果你的业务发展到那个阶段可以考虑在 nginx 生态的基础上引入 OpenResty 或 APISIX、Kong 这类基于 nginx 的 API 网关它们复用 nginx 的高性能内核同时在上层提供了更丰富的扩展能力。还有一部分团队在使用 KubernetesIngress Controller 底层很多就是 nginx。这种情况下粘性会话的能力可以通过 Ingress 注解来配置不一定直接操作 nginx.conf。理解 nginx 底层的粘性会话实现对排查 Ingress 场景下的会话问题依然有帮助毕竟最终干活的是同一套引擎。聊到最后我还是想强调一点任何新版本、新功能都不要急着全量上生产。这次的粘性会话我就是在测试环境先跑了一周覆盖了登录、购物车、文件上传几个核心场景确认 Cookie 流转、故障转移都符合预期之后才在新项目里启用。因为 nginx 是流量入口改出问题影响的就是所有业务。升级前备份好配置规划好回滚路径先把变化控制在最小范围等验证通过再一步步扩大。这比追求新版本带来的性能数字更重要生产环境稳定跑着比什么都值。