作者 劉維
這段時(shí)間在 Reddit 看到一個(gè)討論,為什么 NGINX 不支持熱加載?乍看之下很反常識,作為世界第一大 Web 服務(wù)器,不支持熱加載?難道大家都在使用的 nginx -s reload 命令都用錯(cuò)了?帶著這個(gè)疑問,讓我們開始這次探索之旅,一起聊聊熱加載和 NGINX 的故事。
NGINX 相關(guān)介紹
NGINX 是一個(gè)跨平臺的開源 Web 服務(wù)器,使用 C 語言開發(fā)。據(jù)統(tǒng)計(jì),全世界流量最高的前 1000 名網(wǎng)站中,有超過 40% 的網(wǎng)站都在使用 NGINX 處理海量請求。
NGINX 有什么優(yōu)勢,導(dǎo)致它從眾多的 Web 服務(wù)器中脫穎而出,并一直保持高使用量呢?
我覺得核心原因在于,NGINX 天生善于處理高并發(fā),能在高并發(fā)請求的同時(shí)保持高效的服務(wù)。相比于同時(shí)代的其他競爭對手例如 Apache、Tomcat 等,其領(lǐng)先的事件驅(qū)動型設(shè)計(jì)和全異步的網(wǎng)絡(luò) I/O 處理機(jī)制,以及極致的內(nèi)存分配管理等眾多優(yōu)秀設(shè)計(jì),將服務(wù)器硬件資源壓縮到了極致。使得 NGINX 成為高性能 Web 服務(wù)器的代名詞。
當(dāng)然,除此之外還有一些其他原因,比如:
高度模塊化的設(shè)計(jì),使得 NGINX 擁有無數(shù)個(gè)功能豐富的官方模塊和第三方拓展模塊。
最自由的 BSD 許可協(xié)議,使得無數(shù)開發(fā)者愿意為 NGINX 貢獻(xiàn)自己的想法。
支持熱加載,能保證 NGINX 提供 7x24h 不間斷的服務(wù)。
關(guān)于熱加載
大家期望的熱加載功能是什么樣的?我個(gè)人認(rèn)為,首先應(yīng)該是用戶端無感知的,在保證用戶請求正常和連接不斷的情況下,實(shí)現(xiàn)服務(wù)端或上游的動態(tài)更新。
那什么情況下需要熱加載?在如今云原生時(shí)代下,微服務(wù)架構(gòu)盛行,越來越多的應(yīng)用場景有了更加頻繁的服務(wù)變更需求。包括反向代理域名上下線、上游地址變更、IP 黑白名單更新等,這些都和熱加載息息相關(guān)。
那么 NGINX 是如何實(shí)現(xiàn)熱加載的?
NGINX 熱加載的原理
執(zhí)行 nginx -s reload 熱加載命令,就等同于向 NGINX 的 master 進(jìn)程發(fā)送 HUP 信號。在 master 進(jìn)程收到 HUP 信號后,會依次打開新的監(jiān)聽端口,然后啟動新的 worker 進(jìn)程。
此時(shí)會存在新舊兩套 worker 進(jìn)程,在新的 worker 進(jìn)程起來后,master 會向老的 worker 進(jìn)程發(fā)送 QUIT 信號進(jìn)行優(yōu)雅關(guān)閉。老的 worker 進(jìn)程收到 QUIT 信號后,會首先關(guān)閉監(jiān)聽句柄,此時(shí)新的連接就只會流進(jìn)到新的 worker 進(jìn)程中,老的 worker 進(jìn)程處理完當(dāng)前連接后就會結(jié)束進(jìn)程。
從原理上看,NGINX 的熱加載能很好地滿足我們的需求嗎?答案很可能是否定的,讓我們來看下 NGINX 的熱加載存在哪些問題。
NGINX 熱加載的缺陷
首先,NGINX 頻繁熱加載會造成連接不穩(wěn)定,增加丟失業(yè)務(wù)的可能性。
NGINX 在執(zhí)行 reload 指令時(shí),會在舊的 worker 進(jìn)程上處理已經(jīng)存在的連接,處理完連接上的當(dāng)前請求后,會主動斷開連接。此時(shí)如果客戶端沒處理好,就可能會丟失業(yè)務(wù),這對于客戶端來說明顯就不是無感知的了。
其次,在某些場景下,舊進(jìn)程回收時(shí)間長,進(jìn)而影響正常業(yè)務(wù)。
比如代理 WebSocket 協(xié)議時(shí),由于 NGINX 不解析通訊幀,所以無法知道該請求是否為已處理完畢狀態(tài)。即使 worker 進(jìn)程收到來自 master 的退出指令,它也無法立刻退出,而是需要等到這些連接出現(xiàn)異常、超時(shí)或者某一端主動斷開后,才能正常退出。
再比如 NGINX 做 TCP 層和 UDP 層的反向代理時(shí),它也沒法知道一個(gè)請求究竟要經(jīng)過多少次請求才算真正地結(jié)束。
這就導(dǎo)致舊 worker 進(jìn)程的回收時(shí)間特別長,尤其是在直播、新聞媒體活語音識別等行業(yè)。舊 worker 進(jìn)程的回收時(shí)間通常能達(dá)到半小時(shí)甚至更長,這時(shí)如果再頻繁 reload,將會導(dǎo)致 shutting down 進(jìn)程持續(xù)增加,最終甚至?xí)?dǎo)致 NGINX OOM,嚴(yán)重影響業(yè)務(wù)。
# 一直存在舊 worker 進(jìn)程: nobody 6246 6241 0 10:51 ? 0000 nginx: worker process nobody 6247 6241 0 10:51 ? 0000 nginx: worker process nobody 6247 6241 0 10:51 ? 0000 nginx: worker process nobody 6248 6241 0 10:51 ? 0000 nginx: worker process nobody 6249 6241 0 10:51 ? 0000 nginx: worker process nobody 7995 10419 0 10:30 ? 0037 nginx: worker process is shutting down <= here nobody 7995 10419 0 10:30 ? 0037 nginx: worker process is shutting down nobody 7996 10419 0 10:30 ? 0037 nginx: worker process is shutting down
從上述內(nèi)容可以看到,通過nginx -s reload方式支持的“熱加載”,雖然在以往的技術(shù)場景中夠用,但是在微服務(wù)和云原生迅速發(fā)展的今天,它已經(jīng)捉襟見肘且不合時(shí)宜。
如果你的業(yè)務(wù)變更頻率是每周或者每天,那么 NGINX 這種 reload 還是滿足你的需求的。但如果變更頻率是每小時(shí)、每分鐘呢?假設(shè)你有 100 個(gè) NGINX 服務(wù),每小時(shí) reload 一次的話,就要 reload 2400 次;如果每分鐘 reload 一次,就是 864 萬次。這顯然是無法接受的。
因此,我們需要一個(gè)不需要進(jìn)程替換的 reload 方案,在現(xiàn)有 NGINX 進(jìn)程內(nèi)可以直接完成內(nèi)容的更新和實(shí)時(shí)生效。
在內(nèi)存中直接生效的熱加載方案
在 Apache APISIX 誕生之初,就是希望來解決 NGINX 熱加載這個(gè)問題的。
Apache APISIX 是基于 NGINX + Lua 的技術(shù)棧,以 ETCD 作為配置中心實(shí)現(xiàn)的云原生、高性能、全動態(tài)的微服務(wù) API 網(wǎng)關(guān),提供負(fù)載均衡、動態(tài)上游、灰度發(fā)布、精細(xì)化路由、限流限速、服務(wù)降級、服務(wù)熔斷、身份認(rèn)證、可觀測性等數(shù)百項(xiàng)功能。
使用 APISIX 你不需要重啟服務(wù)就可以更新配置,這意味著修改上游、路由、插件時(shí)都不用重啟。既然是基于 NGINX,APISIX 又是如何擺脫 NGINX 的限制實(shí)現(xiàn)完美熱更新?我們先看下 APISIX 的架構(gòu)。
通過上述架構(gòu)圖可以看到,之所以 APISIX 能擺脫 NGINX 的限制是因?yàn)樗焉嫌蔚扰渲萌糠诺?APISIX Core 和 Plugin Runtime 中動態(tài)指定。
以路由為例,NGINX 需要在配置文件內(nèi)進(jìn)行配置,每次更改都需要 reload 之后才能生效。而為了實(shí)現(xiàn)路由動態(tài)配置,Apache APISIX 在 NGINX 配置文件內(nèi)配置了單個(gè) server,這個(gè) server 中只有一個(gè) location。我們把這個(gè) location 作為主入口,所有的請求都會經(jīng)過這個(gè) location,再由 APISIX Core 動態(tài)指定具體上游。因此 Apache APISIX 的路由模塊支持在運(yùn)行時(shí)增減、修改和刪除路由,實(shí)現(xiàn)了動態(tài)加載。所有的這些變化,對客戶端都零感知,沒有任何影響。
再來幾個(gè)典型場景的描述。
比如增加某個(gè)新域名的反向代理,在 APISIX 中只需創(chuàng)建上游,并添加新的路由即可,整個(gè)過程中不需要 NGINX 進(jìn)程有任何重啟。再比如插件系統(tǒng),APISIX 可以通過 ip-restriction 插件實(shí)現(xiàn) IP 黑白名單功能,這些能力的更新也是動態(tài)方式,同樣不需要重啟服務(wù)。借助架構(gòu)內(nèi)的 ETCD,配置策略以增量方式實(shí)時(shí)推送,最終讓所有規(guī)則實(shí)時(shí)、動態(tài)的生效,為用戶帶來極致體驗(yàn)。
總結(jié)
NGINX 的熱加載在某些場景下會長時(shí)間存在新舊兩套進(jìn)程,導(dǎo)致額外消耗資源,同時(shí)頻繁熱加載也會導(dǎo)致小概率業(yè)務(wù)丟失。面對當(dāng)下云原生和微服務(wù)的技術(shù)趨勢下, 服務(wù)變化更加的頻繁,控制 API 的策略也發(fā)生了變化,導(dǎo)致我們對熱加載的需求提出了新需求,NGINX 的熱加載已經(jīng)不能滿足實(shí)際業(yè)務(wù)需求。
現(xiàn)在是時(shí)候切換到更貼合云原生時(shí)代并且更完善的熱加載策略、性能表現(xiàn)卓越的 API 網(wǎng)關(guān)——Apache APISIX,從而享受動態(tài)、統(tǒng)一管理等特性帶來的管理效率上的極大提升。
審核編輯:湯梓紅
-
Web
+關(guān)注
關(guān)注
2文章
1263瀏覽量
69508 -
服務(wù)器
+關(guān)注
關(guān)注
12文章
9184瀏覽量
85489 -
命令
+關(guān)注
關(guān)注
5文章
684瀏覽量
22037 -
nginx
+關(guān)注
關(guān)注
0文章
150瀏覽量
12184
原文標(biāo)題:為什么NGINX的reload命令不是熱加載?
文章出處:【微信號:OSC開源社區(qū),微信公眾號:OSC開源社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論