化實(shí)踐)
nginx 1.29.6 發(fā)布了作為主線版本這算得上是今年值得仔細(xì)看的一次更新。我在生產(chǎn)環(huán)境里維護(hù)著一批 nginx 網(wǎng)關(guān)從 1.16 一路追到 1.28每次主線版本發(fā)版我都會先翻 changelog再拿測試機(jī)驗(yàn)證確認(rèn)沒有坑之后才考慮要不要跟。這次 1.29.6 最讓我關(guān)注的有兩件事一是 upstream 終于支持原生粘性會話二是性能與穩(wěn)定性方向有一批改進(jìn)。這兩個點(diǎn)對搞負(fù)載均衡、多節(jié)點(diǎn)部署的朋友來說屬于能直接改變架構(gòu)選型的內(nèi)容。粘性會話這個詞搞過后端的人應(yīng)該不陌生。應(yīng)用多節(jié)點(diǎn)部署之后用戶的登錄狀態(tài)、購物車數(shù)據(jù)、臨時上傳進(jìn)度都存在某臺服務(wù)器上如果負(fù)載均衡把請求隨機(jī)分到不同節(jié)點(diǎn)輕則用戶反復(fù)掉線重則直接把業(yè)務(wù)打崩。以前 nginx 官方不支持這個能力大家只能用第三方模塊或者用 ip_hash、hash 指令硬湊?,F(xiàn)在 1.29.6 官方主線直接支持意味著以后在標(biāo)準(zhǔn)環(huán)境里就能做會話保持不用再為了一個 sticky 模塊去編譯維護(hù)一堆第三方代碼了。這篇文章我會先把這次更新的核心內(nèi)容拆開講清楚然后重點(diǎn)聊粘性會話的原理和配置再補(bǔ)上性能調(diào)優(yōu)和升級后的常見坑。適合正在用 nginx 做反向代理和負(fù)載均衡的運(yùn)維、后端開發(fā)以及準(zhǔn)備升級主線版本但心里沒底的團(tuán)隊(duì)參考。1. 1.29.6 的核心看點(diǎn)官方粘性會話為何等了這么久1.1 為什么粘性會話此前一直靠第三方模塊nginx 官方在很長一段時間里反代負(fù)載均衡只有輪詢、權(quán)重、ip_hash、最少連接這幾種策略。這些策略的共同點(diǎn)是“負(fù)載均衡優(yōu)先”每個請求盡量均勻分發(fā)給后端不關(guān)心請求來自哪個用戶、和之前的請求有沒有關(guān)聯(lián)。這在純 API 網(wǎng)關(guān)場景沒問題但一旦涉及 Session、Cookie、長連接就很容易捅婁子。社區(qū)很早就有解決方案。老牌的 nginx-sticky-module-ng很多人編譯過稍微新一點(diǎn)的做法是用 hash 指令自己按 Cookie 或 Header 做一致性哈希。但這些方案都有比較明顯的毛病。第三方模塊要跟隨 nginx 版本重新編譯每次 nginx 升級模塊編譯失敗的情況我都遇到過不止一次。hash 方案呢能用但對節(jié)點(diǎn)增刪不友好調(diào)整后端的瞬間會有大量請求漂移到其他節(jié)點(diǎn)Session 直接丟失。這背后其實(shí)有 nginx 官方的取舍。官方對核心模塊的穩(wěn)定性要求極高一個新特性要進(jìn)主線得先解決權(quán)限模型、模塊加載機(jī)制、與健康檢查的協(xié)作方式等一系列問題。粘性會話不像輪詢那樣無狀態(tài)它天然引入“用戶和后端節(jié)點(diǎn)綁定”的概念這會和故障轉(zhuǎn)移產(chǎn)生沖突——如果用戶綁定的那臺后端掛了請求到底該轉(zhuǎn)發(fā)到哪里這個問題處理不好官方寧可不做。所以這次 1.29.6 上線粘性會話從產(chǎn)品決策到協(xié)議設(shè)計都花了不少心思。1.2 新增粘性會話后哪些場景直接受益第一類明顯受益的場景是帶登錄態(tài)的傳統(tǒng) Web 應(yīng)用。比如企業(yè)內(nèi)部系統(tǒng)、CMS、電商網(wǎng)站用戶登錄之后 Session 存在本機(jī)內(nèi)存里Session 沒有做共享這種架構(gòu)下粘性會話幾乎是剛需。以前沒有官方支持要么在應(yīng)用層做 Session 復(fù)制要么引入 Redis 共享 Session要么就忍著用戶隨機(jī)掉線?,F(xiàn)在只需要在 nginx 層做一次配置把同一個用戶的請求固定到同一臺后端節(jié)點(diǎn)應(yīng)用層基本不用動。第二類是 WebSocket 和長連接場景。nginx 代理 WebSocket 已經(jīng)非常成熟但長連接比普通 HTTP 請求更需要會話保持。一個 WebSocket 連接建立之后后端的進(jìn)程和前端是保持雙向通信的一旦連接被轉(zhuǎn)發(fā)到另一臺節(jié)點(diǎn)這條鏈路就會斷裂。比如用 nginx 代理 FreeSWITCH 的 WS 端口、IM 服務(wù)的推送通道這類業(yè)務(wù)天然需要粘性會話。第三類是文件上傳下載、分片傳輸這類有狀態(tài)流。大文件分片上傳每一片都希望落到同一臺后端否則合并的時候找誰要數(shù)據(jù)粘性會話能把同一用戶的流式請求穩(wěn)穩(wěn)固定在一臺節(jié)點(diǎn)上省掉很多應(yīng)用層的合并邏輯。1.3 性能與穩(wěn)定性提升的實(shí)際價值其實(shí)每次主線版本更新changelog 里都會有一批性能優(yōu)化和穩(wěn)定性修復(fù)。這種更新通常不會大張旗鼓宣傳但對生產(chǎn)環(huán)境的影響非常直接。從我追蹤版本的經(jīng)驗(yàn)來看這類改動主要集中在這幾個方向。一是連接生命周期相關(guān)的問題。包括連接復(fù)用、空閑連接回收、異常連接釋放等。生產(chǎn)環(huán)境中經(jīng)常出現(xiàn)的 TIME_WAIT 堆積、worker 連接數(shù)緩慢上漲、長時間運(yùn)行后連接池膨脹基本都是這類問題引起的。新版本往往會在 upstream 連接的復(fù)用策略和等待隊(duì)列上做調(diào)整讓整個并發(fā)連接模型更平滑。二是 HTTP/2 和 HTTP/3 的實(shí)現(xiàn)完善。HTTP/2 已經(jīng)普及但多路復(fù)用下的流量控制問題、連接并發(fā)導(dǎo)致的性能回退問題不同版本的表現(xiàn)差異很大。HTTP/3 也就是 QUIC 協(xié)議nginx 一直在推進(jìn)每一次主線更新都會帶上 QUIC 相關(guān)的修復(fù)和改進(jìn)。如果你打算開啟 HTTP/3這類底層的協(xié)議棧完善對穩(wěn)定性幫助很大。三是內(nèi)存使用和 worker 進(jìn)程的穩(wěn)定性。nginx 對內(nèi)存的控制一直很嚴(yán)格但應(yīng)對極端流量、大量非法請求、畸形報文的時候偶發(fā)的高內(nèi)存占用和 worker 崩潰也沒有完全絕跡。主線版本通常會在這些邊緣場景上打補(bǔ)丁所以“穩(wěn)定性全面提升”這種說法其實(shí)是落到這些具體場景里的。2. 粘性會話的原理與選型從 Cookie 到一致性哈希2.1 粘性會話解決的核心矛盾負(fù)載均衡的核心邏輯是把流量分散開讓每臺后端都能吃飽但又不能撐死。問題來了請求可以隨便分用戶的數(shù)據(jù)狀態(tài)卻不能隨便分。用戶登錄之后Session 存在 A 節(jié)點(diǎn)下一次請求如果被負(fù)載均衡分到 B 節(jié)點(diǎn)B 節(jié)點(diǎn)查不到這個 Session就只能讓用戶重新登錄。粘性會話的思路很簡單在負(fù)載均衡層加一個“綁定關(guān)系”讓同一個用戶的請求盡量落到同一臺后端。這與 Session 共享是兩種不同的解決路徑。Session 共享是讓所有節(jié)點(diǎn)都能訪問同一份狀態(tài)屬于“數(shù)據(jù)搬家”粘性會話則是把用戶綁死在固定的節(jié)點(diǎn)上屬于“人不搬家”。兩種方式?jīng)]有絕對的好壞但粘性會話在改動成本上更低尤其適合改造老系統(tǒng)。只要 nginx 層配置一下業(yè)務(wù)代碼完全不用動不香嗎2.2 Cookie 粘性如何工作Cookie 粘性是實(shí)現(xiàn)粘性會話最常見的方式。流程大概是這樣的用戶第一次訪問某個服務(wù)nginx 所在的負(fù)載均衡層發(fā)現(xiàn)這個請求沒有攜帶粘性 Cookie就按照當(dāng)前策略選擇一臺后端節(jié)點(diǎn)接收請求并在響應(yīng)里種一個 Cookie比如 srv_idbackend-a。用戶接下來的請求都會帶著這個 Cookienginx 看到 Cookie 里的值之后直接把請求轉(zhuǎn)發(fā)到 backend-a完成綁定。這里有個細(xì)節(jié)需要理解nginx 并不需要真的去后端存儲里確認(rèn)這個 Cookie 是否有效。它維護(hù)的是一個映射關(guān)系即“某個 Cookie 值對應(yīng)哪個后端節(jié)點(diǎn)”。這個映射可以放在共享內(nèi)存里也可以把后端節(jié)點(diǎn)信息直接編碼在種下去的 Cookie 值中后者避免了大量連接狀態(tài)占用內(nèi)存。生產(chǎn)環(huán)境里更推薦后一種方式即使 nginx 重啟、worker 重新調(diào)度Cookie 值仍然有效不影響粘性。還有一類是基于一致性哈希的實(shí)現(xiàn)。不種 Cookie而是根據(jù)請求里的某個穩(wěn)定標(biāo)識比如用戶 ID、來源 IP、特定的 Header做哈希計算算出來的結(jié)果就是后端節(jié)點(diǎn)的編號。這種方式的優(yōu)點(diǎn)是沒有額外的 Cookie也不依賴瀏覽器是否支持 Cookie缺點(diǎn)是如果后端節(jié)點(diǎn)數(shù)量變化哈希結(jié)果會大面積漂移。2.3 幾種粘性策略怎么選不同策略的選型主要看你的業(yè)務(wù)形態(tài)和現(xiàn)有基礎(chǔ)設(shè)施我整理了一個對比表格策略實(shí)現(xiàn)方式優(yōu)點(diǎn)缺點(diǎn)適用場景Cookie 粘性種 Cookie 并映射到節(jié)點(diǎn)精確、用戶經(jīng)驗(yàn)好、不受代理 IP 影響依賴瀏覽器 Cookie無 Cookie 客戶端不適用帶登錄態(tài)的 Web 應(yīng)用、購物車、企業(yè)系統(tǒng)ip_hash按來源 IP 哈希無需額外配置天然支持多出口 IP 會把用戶全塞到一臺節(jié)點(diǎn)出口 IP 單一的內(nèi)網(wǎng)服務(wù)、IPv6 場景Hash Key按 Header/參數(shù)/指定 key 哈希靈活可針對業(yè)務(wù)標(biāo)識進(jìn)行哈希節(jié)點(diǎn)變更時哈希漂移Session 容易丟無 Cookie 的 API 網(wǎng)關(guān)、需要按租戶隔離的場景第三方 sticky 模塊編譯第三方模塊功能成熟可自定義升級跟隨成本高模塊質(zhì)量參差不齊舊環(huán)境暫時無法升級到新版本我在實(shí)際項(xiàng)目里給電商類站點(diǎn)用的是 Cookie 粘性種一個短期 Cookie 就好給內(nèi)部 API 網(wǎng)關(guān)用的是基于 Header 的哈希比如按 X-User-Id 哈希不用管 Cookie也不會因?yàn)闉g覽器禁用 Cookie 而失效。這個選擇邏輯很簡單有明確用戶身份的優(yōu)先用 Cookie 粘性按來源 IP 和入口網(wǎng)關(guān)做聚合的就用 ip_hash 或 Hash Key。3. 升級 1.29.6 與粘性會話配置實(shí)操3.1 升級前的檢查與備份升級之前我習(xí)慣先做三件事。第一明確當(dāng)前版本。命令是nginx -v如果發(fā)現(xiàn)當(dāng)前是某個很老的穩(wěn)定版跳版本升級要格外注意配置兼容性。第二完整備份配置。不僅僅是復(fù)制一份 nginx.conf而是把 conf.d 下的所有 conf 文件、證書目錄、還有編譯時的模塊列表全部記錄下來。第三確認(rèn)回滾路徑。git 里留一份當(dāng)前可用的 nginx 二進(jìn)制和配置萬一新版本有兼容性問題能在一分鐘內(nèi)回到舊版本。還有一點(diǎn)容易被忽略就是確認(rèn)第三方模塊兼容性。如果你當(dāng)前環(huán)境編譯過第三方模塊升級到 1.29.6 之后這些模塊大概率要重新編譯。有些模塊原作者已經(jīng)不怎么維護(hù)了在新版本上編譯可能直接報錯。建議升級前先在新版本源碼上跑一次./configure加上原有參數(shù)看看模塊能不能編譯通過。這一步卡住的話就別急著上生產(chǎn)先解決模塊兼容性問題。3.2 平滑升級的完整流程如果你用的是官方源碼編譯方式平滑升級流程相對成熟。先把 1.29.6 的源碼包下載下來解壓沿用之前的編譯參數(shù)重新 configure 并編譯新二進(jìn)制然后用新二進(jìn)制替換舊二進(jìn)制。保險起見替換前可以把舊二進(jìn)制備份為 nginx.old。替換完成后執(zhí)行kill -USR2 舊master進(jìn)程PID讓新老版本同時運(yùn)行再kill -WINCH 舊master進(jìn)程PID優(yōu)雅關(guān)閉舊 worker最后kill -QUIT 舊master進(jìn)程PID退出舊的 master。用系統(tǒng)自帶軟件源安裝的 nginx升級方式會更簡單直接執(zhí)行包管理器的升級命令就行。但要注意RedHat 系和 Debian 系默認(rèn)的 nginx 版本往往比主線版落后不少。如果你等不到官方源更新可以自行添加 nginx 官方源或者直接使用編譯安裝的方式。我這里更推薦編譯安裝因?yàn)榭梢跃_控制編譯參數(shù)還能把第三方模塊一起編進(jìn)去。升級完成之后我的驗(yàn)證順序是nginx -t校驗(yàn)配置nginx -V確認(rèn)版本和編譯參數(shù)然后 reload 一次觀察 error.log 有沒有新增告警再通過壓測或者線上小流量驗(yàn)證基礎(chǔ)功能。3.3 配置一個帶粘性會話的 upstream假設(shè)我們有兩個后端節(jié)點(diǎn)部署了同一個 Java Web 應(yīng)用用戶登錄狀態(tài)存在本機(jī) Session。配置粘性會話之后同一用戶的請求會穩(wěn)定落到同一臺節(jié)點(diǎn)。生產(chǎn)環(huán)境下的配置大致長這樣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說明你使用的這個版本可能還沒編譯粘性會話模塊或者指令名有細(xì)微差異。畢竟 nginx 不同版本的模塊名和參數(shù)會有調(diào)整保險做法是先看當(dāng)前版本的官方文檔確認(rèn)模塊名再按文檔語法來配置。我在測試機(jī)上試過第一次配置時就是因?yàn)橹噶蠲麑戝e導(dǎo)致 nginx -t 直接報錯。另一個關(guān)鍵參數(shù)是expires。Cookie 粘性會給客戶端種一個 Cookie這個 Cookie 的過期時間要設(shè)計好。太短的話用戶沒過多久就失效還得重新綁定太長的話后端節(jié)點(diǎn)要下線維護(hù)時用戶可能還掛在舊節(jié)點(diǎn)上請求頻繁失敗。我的經(jīng)驗(yàn)是業(yè)務(wù) Session 持續(xù)多久Cookie 就設(shè)置多久最多不要超過一天。電商場景設(shè)置 30 分鐘到 1 小時比較合適管理后臺這類長會話可以設(shè)置 8 小時。3.4 驗(yàn)證粘性是否生效的兩種方法配置完粘性會話怎么確認(rèn)是真的生效了我常用的方法是先看 Set-Cookie 響應(yīng)頭再用帶 Cookie 的請求做連續(xù)性驗(yàn)證。第一步不帶 Cookie 請求一次登錄接口打開抓包工具或者直接用 curlcurl -sI http://app.example.com/login | grep -i set-cookie正常響應(yīng)里應(yīng)該能看到 nginx 下發(fā)的 Cookie比如srv_idbackend-a。如果看到的是業(yè)務(wù)自己種的其他 Cookie沒有 srv_id說明粘性模塊可能沒生效或者被業(yè)務(wù)層覆蓋了。第二步把第一步拿到的 Cookie 值原樣帶回去連續(xù)請求幾次業(yè)務(wù)接口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 或者響應(yīng)時間兩個請求應(yīng)該都落在 backend-a 節(jié)點(diǎn)上。如果第二次請求落到了 backend-b說明粘性配置有問題或者 Cookie 在瀏覽器側(cè)被清了。多節(jié)點(diǎn)場景最好給每個后端節(jié)點(diǎn)配置獨(dú)立的 access_log方便做這種驗(yàn)證。4. 性能與穩(wěn)定性調(diào)優(yōu)的落地姿勢4.1 長連接與 keepalive 的正確參數(shù)nginx 1.29.6 這類主線版本在 upstream 長連接處理上通常會有改進(jìn)但你的配置如果一直沒調(diào)再好的改動也發(fā)揮不出來。之前排查過一次線上問題后端 Tomcat 的連接數(shù)經(jīng)常被打滿后來發(fā)現(xiàn) nginx 每次請求都重新建連沒有啟用 upstream keepalive。補(bǔ)上之后連接數(shù)和后端負(fù)載同時降了不少。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 ; } }這里有兩個容易忽略的細(xì)節(jié)。第一必須設(shè)置proxy_http_version 1.1否則 nginx 到后端默認(rèn)走 HTTP/1.0連接無法復(fù)用。第二proxy_set_header Connection 必須清空不然頭信息里留著 Connection: closekeepalive 照樣不生效。這兩個點(diǎn)少一個keepalive 就等于沒配。keepalive 參數(shù)也不是越大越好。32 條空閑連接看后端能承受多少。后端 Tomcat 并發(fā)線程通常就 200 左右你在一臺 nginx 上 keepalive 設(shè)置 100意味著每臺后端要維護(hù) 100 條空閑連接后端連接池的負(fù)擔(dān)會很大。合理的設(shè)置是先看后端連接池上限再給 nginx 配一個合適的值一般 16 到 64 之間比較穩(wěn)妥。4.2 HTTP/3 要不要現(xiàn)在就上1.29.6 主線版本里 HTTP/3 和 QUIC 的支持又往前走了一步很多朋友已經(jīng)開始糾結(jié)要不要開啟。我的判斷是HTTP/3 確實(shí)能提升弱網(wǎng)環(huán)境下的用戶體驗(yàn)尤其針對移動端和跨地域用戶但生產(chǎn)環(huán)境直接全量上線安全隱患和兼容性問題都不少。最常見的坑是中間設(shè)備攔截。運(yùn)營商網(wǎng)絡(luò)或者企業(yè)防火墻里有些老設(shè)備不認(rèn)識 QUIC 報文直接把 UDP 443 丟掉導(dǎo)致瀏覽器報net::err_quic_protocol_error。這個問題在辦公網(wǎng)、校園網(wǎng)尤其明顯。另一個坑是監(jiān)控體系。HTTP/3 的請求在抓包工具和 Web 日志里的形態(tài)和 TCP 完全不一樣監(jiān)控、日志分析都要跟著調(diào)整如果監(jiān)控沒跟上出了問題定位會很痛苦。我建議的做法是漸進(jìn)式開啟。先在某個測試域名或者對部分用戶開啟 HTTP/3觀察訪問成功率、報錯率、頁面加載時間跑一段時間穩(wěn)定之后再逐步擴(kuò)大流量。如果業(yè)務(wù)對網(wǎng)絡(luò)穩(wěn)定性要求極高暫時不開也不丟人HTTP/2 TLS1.3 在絕大多數(shù)場景下已經(jīng)夠用了。4.3 SSL 握手性能優(yōu)化清單升級到新版本之后SSL/TLS 相關(guān)的優(yōu)化也值得重新過一遍。nginx 默認(rèn)配置在手寫環(huán)境下往往沒有展示出真正的性能幾個改動能明顯減少握手時間和 CPU 占用。第一個是ssl_session_cache。這個參數(shù)非常關(guān)鍵它讓 nginx 在本地緩存 TLS 會話票據(jù)后續(xù)客戶端重新建連可以直接復(fù)用省掉一次完整的 TLS 握手。生產(chǎn)環(huán)境建議設(shè)置為shared:SSL:10m10MB 共享內(nèi)存大約能存 8 萬多個會話。如果你發(fā)現(xiàn)客戶端頻繁握手、頁面加載偏慢多半是這個沒設(shè)好。第二個是ssl_protocols和ssl_ciphers。建議只開啟 TLSv1.2 和 TLSv1.3把 SSLv3、TLSv1.0、TLSv1.1 全部關(guān)閉。TLSv1.3 在握手輪次和加密效率上比老版本強(qiáng)太多只要客戶端兼容優(yōu)先開啟。這個操作放在 nginx 配置里只是一個指令但對安全合規(guī)和性能都有實(shí)際提升。第三個是證書鏈優(yōu)化。證書鏈里有中間證書和根證書nginx 每次握手要發(fā)送整條證書鏈。證書鏈太長、證書文件過大握手時間就會增加。把證書文件整理成 PEM 格式并檢查證書鏈完整性雖然不起眼但訪問速度會有體感提升。5. 升級后常見問題與排查實(shí)錄5.1 排查工具與應(yīng)急思路升級過程中不管做多少準(zhǔn)備上線后還是可能出問題。我的排查思路一般都是自底向上先看 error.log再看 access.log最后才動具體請求驗(yàn)證。查看 error.log 時建議帶上時間線定位到一個比較長的時間窗口用tail -f或者grep篩選關(guān)鍵字。常見的錯誤信息格式大概是[error]、[crit]、[emerg]等嚴(yán)重級別從低到高。[emerg]出現(xiàn)意味著配置錯誤nginx 起不來[crit]出現(xiàn)在磁盤寫滿、內(nèi)存不足等系統(tǒng)級問題[error]則多是業(yè)務(wù)請求相關(guān)的錯誤。看到報錯先別急著百度先認(rèn)真讀一遍日志內(nèi)容大多數(shù)情況下報錯信息已經(jīng)很明確了。排查連接問題時nginx -T非常有用。它可以導(dǎo)出 nginx 加載后的完整配置比直接看散落的配置文件更清楚能看清sticky、keepalive、proxy_pass這些指令最終生效的值避免因?yàn)?include 引入多個配置文件時出現(xiàn)覆蓋和沖突。配合curl -v看請求頭能直接確認(rèn) Cookie、響應(yīng)頭是否符合預(yù)期。5.2 高頻問題速查表問題可能原因解決辦法配置 sticky 后 nginx -t 報 unknown directive粘性模塊未編譯或模塊名寫錯確認(rèn)當(dāng)前版本是否包含對應(yīng)模塊查閱官方文檔修正指令名粘性會話不生效用戶反復(fù)掉線瀏覽器禁用了 Cookie、多個域名間 Cookie 未共享、代理層多次轉(zhuǎn)發(fā)檢查 Set-Cookie 路徑和域名用 curl 模擬帶 Cookie 請求驗(yàn)證后端節(jié)點(diǎn)添加或刪除后大量 Session 丟失節(jié)點(diǎn)變更導(dǎo)致哈希映射漂移舊用戶被重定向到新節(jié)點(diǎn)規(guī)劃好維護(hù)窗口結(jié)合一致性哈希的前綴保留策略逐步調(diào)整升級后部分接口 403/404配置文件不兼容server_name、location 匹配規(guī)則有變化grep 對比新舊配置文件差異重點(diǎn)檢查 location 和 rewrite 規(guī)則開啟 HTTP/3 后瀏覽器訪問報 err_quic_protocol_error中間網(wǎng)絡(luò)設(shè)備攔截 UDP 443 報文暫時關(guān)閉 HTTP/3或限制到測試域名小流量驗(yàn)證高并發(fā)下 error.log 出現(xiàn) worker_connections are not enoughworker_connections 設(shè)置過小根據(jù)系統(tǒng) ulimit 調(diào)大 worker_connections配合調(diào)整 worker_processesreload 后連接瞬間斷掉worker 進(jìn)程重啟長連接被回收業(yè)務(wù)側(cè)實(shí)現(xiàn)重連機(jī)制避免在流量高峰時 reload5.3 幾個來自實(shí)踐的避坑經(jīng)驗(yàn)第一升級前一定要在一臺獨(dú)立測試機(jī)跑一遍全量回歸。我在一次升級中吃過虧新版本配置校驗(yàn)通過但舊配置里的某個第三方指令在新版本中行為變化了上線后即時接口出現(xiàn)了路徑漂移?;貧w用例要覆蓋至少一遍登錄、上傳、WebSocket 連接等核心場景不能只壓測不驗(yàn)功能。第二粘性會話的 Cookie 名稱不要和業(yè)務(wù) Cookie 沖突。業(yè)務(wù)本身可能種了一個名為 srv_id 的 Cookienginx 又種了一個同名 Cookie后種的值會覆蓋前種的值導(dǎo)致粘性失效。我在一次配置時把粘性 Cookie 命名為 app_srv_id從源頭上避免沖突。第三升級后不要立即清空舊版本二進(jìn)制。保留 nginx.old保留舊配置備份至少保留到新版本穩(wěn)定運(yùn)行一周以上再清理。真出了兼容性問題回滾路徑遠(yuǎn)比修復(fù)問題更可靠。6. 從 1.29.6 看主線版本選型與演進(jìn)6.1 主線版和穩(wěn)定版怎么選nginx 的版本發(fā)布節(jié)奏里主線版本和穩(wěn)定版本是并行的。主線版本更新頻繁新功能都在主線里先出現(xiàn)但并不意味著不穩(wěn)定只是更新迭代速度快。穩(wěn)定版本更保守只做嚴(yán)重安全修復(fù)和 bug 修復(fù)不引入新功能。很多團(tuán)隊(duì)對主線版本有心理顧慮覺得“穩(wěn)定版才適合生產(chǎn)”這個觀念在早期確實(shí)是主流但現(xiàn)在 nginx 官方對主線版本的定位已經(jīng)非常成熟生產(chǎn)環(huán)境用主線版本的團(tuán)隊(duì)越來越多。如果你所在團(tuán)隊(duì)對新技術(shù)接受度較高且有一定的測試能力我建議直接跟隨主線版本。粘性會話、HTTP/3 改進(jìn)這些新東西只有主線版本才有穩(wěn)定版要等很久才會合入。如果你所在的行業(yè)對變更極度敏感或者你只有一臺核心網(wǎng)關(guān)、沒有測試環(huán)境那還是保守一點(diǎn)等穩(wěn)定版發(fā)布之后驗(yàn)證沒問題再升級也不遲。我的個人習(xí)慣是測試環(huán)境永遠(yuǎn)用最新主線版本生產(chǎn)環(huán)境比主線版本滯后一個版本等社區(qū)反饋穩(wěn)定之后再跟進(jìn)。這樣既不會錯過新特性又能用社區(qū)的問題反饋?zhàn)鳛楸芸右罁?jù)。6.2 nginx 在網(wǎng)關(guān)生態(tài)里的位置從 1.29.6 這次的更新其實(shí)能看到 nginx 的演進(jìn)方向官方對網(wǎng)關(guān)層能力越來越重視。粘性會話過去是商業(yè)版 nginx Plus 的賣點(diǎn)之一現(xiàn)在進(jìn)了主線說明官方在把一些高頻需求逐步下沉到開源版本里。這對開發(fā)者來說肯定是個好消息。但也要清醒看到nginx 核心的定位始終是高性能反向代理和 Web 服務(wù)器它不會變成一個功能冗雜的應(yīng)用網(wǎng)關(guān)。像灰度發(fā)布、熔斷限流、服務(wù)發(fā)現(xiàn)集成、自定義鑒權(quán)這類能力純 nginx 配置會越寫越復(fù)雜維護(hù)成本很高。如果你的業(yè)務(wù)發(fā)展到那個階段可以考慮在 nginx 生態(tài)的基礎(chǔ)上引入 OpenResty 或 APISIX、Kong 這類基于 nginx 的 API 網(wǎng)關(guān)它們復(fù)用 nginx 的高性能內(nèi)核同時在上層提供了更豐富的擴(kuò)展能力。還有一部分團(tuán)隊(duì)在使用 KubernetesIngress Controller 底層很多就是 nginx。這種情況下粘性會話的能力可以通過 Ingress 注解來配置不一定直接操作 nginx.conf。理解 nginx 底層的粘性會話實(shí)現(xiàn)對排查 Ingress 場景下的會話問題依然有幫助畢竟最終干活的是同一套引擎。聊到最后我還是想強(qiáng)調(diào)一點(diǎn)任何新版本、新功能都不要急著全量上生產(chǎn)。這次的粘性會話我就是在測試環(huán)境先跑了一周覆蓋了登錄、購物車、文件上傳幾個核心場景確認(rèn) Cookie 流轉(zhuǎn)、故障轉(zhuǎn)移都符合預(yù)期之后才在新項(xiàng)目里啟用。因?yàn)?nginx 是流量入口改出問題影響的就是所有業(yè)務(wù)。升級前備份好配置規(guī)劃好回滾路徑先把變化控制在最小范圍等驗(yàn)證通過再一步步擴(kuò)大。這比追求新版本帶來的性能數(shù)字更重要生產(chǎn)環(huán)境穩(wěn)定跑著比什么都值。