今天這篇文章主要來介紹下 Nginx 的 reload 流程。實(shí)際上在之前文章中,在更改了 nginx 配置文件時(shí),我們都會執(zhí)行 nginx -s reload 命令,我們執(zhí)行這條命令的原因是希望 nginx 不停止服務(wù)始終在處理新的請求的同時(shí)把 nginx 的配置文件平滑的把舊的 nginx.conf 配置更新為新的 nginx.conf 配置。需要看Nginx架構(gòu)可以看這篇(Nginx 優(yōu)秀的核心架構(gòu)設(shè)計(jì)揭秘,讓你搞懂高并發(fā)之道)。
這樣一個(gè)功能對于 nginx 非常有必要,但是有時(shí)候我們會發(fā)現(xiàn)在執(zhí)行 nginx -s reload 命令后,worker 子進(jìn)程的數(shù)量會變多了,這是因?yàn)槔系呐渲眠\(yùn)行的 worker 進(jìn)程長時(shí)間沒有退出,當(dāng)使用 stream 做四層反向代理的時(shí)候,可能這種場景會更多。
那么下面我們通過分析 nginx 的 reload 流程,來探究下 nginx 到底做了些什么?所謂優(yōu)雅的退出和立即退出有什么區(qū)別?
reload 流程

第一步在修改好 nginx 的配置文件 nginx.conf 后,向 master 進(jìn)程發(fā)送 HUP 信號,這實(shí)際上和我們在命令行執(zhí)行 nginx -s reload 命令效果是一樣的。
那么 master 進(jìn)程在收到 HUP 信號以后,會在第二步檢查我們的配置文件語法是否正確,也就是說我們并不一定非要在 nginx -s reload 前執(zhí)行 nginx -t 檢驗(yàn)下語法是否正確,因?yàn)樵诘诙?nginx 的 master 進(jìn)程一定會執(zhí)行這個(gè)步驟。
在 nginx 的配置語法全部正確以后,master 進(jìn)程會打開新的監(jiān)聽端口,為什么要在 master 進(jìn)程中打開新的監(jiān)聽端口?因?yàn)槲覀兛赡茉?nginx.conf 中會引入新的例如 443 或者之前我們沒有打開的的監(jiān)聽端口,而所有 worker 進(jìn)程是 master 進(jìn)程 的子進(jìn)程,子進(jìn)程會繼承父進(jìn)程所有已經(jīng)打開的端口,這是 linux 操作系統(tǒng)定義的,所以第三步,我們 master 進(jìn)程打開了可能引入的新的監(jiān)聽端口。
接下來 mster 進(jìn)程會用新的 nginx.conf 配置文件來啟動新的 worker 子進(jìn)程,那么老的 worker 子進(jìn)程會怎么樣呢?
我們會在第五步在啟動新的 worker 子進(jìn)程以后,由 master 進(jìn)程再向老 worker 子進(jìn)程發(fā)送 QUIT 信號,QUIT 信號和 TERM,INT 信號是不一樣的,QUIT 信號是請優(yōu)雅地關(guān)閉子進(jìn)程,這時(shí)候需要關(guān)注順序,因?yàn)?nginx 需要保證平滑,所以要先啟動新的 worker 子進(jìn)程,再向老的 worker 子進(jìn)程發(fā)送 QUIT 信號。
那么老的 master 子進(jìn)程收到 QUIT 信號后,首先關(guān)閉監(jiān)聽句柄,也就是說這個(gè)時(shí)候新的連接只會到新的 worker 子進(jìn)程,所以雖然他們之間有時(shí)間差,但是時(shí)間是非??焖俚?,那么關(guān)閉監(jiān)聽句柄后,處理完當(dāng)前連接后就結(jié)束進(jìn)程。
下面看 reload 不停機(jī)載入新配置的圖示。
reload 不停機(jī)載入新配置

master 進(jìn)程上原先有四個(gè)綠色的 worker 子進(jìn)程,它們使用了老的配置,當(dāng)我們更改了 nginx.conf 配置文件后,向 master 發(fā)送 SIGHUP 信號或者執(zhí)行 reload 命令, 然后 master 會用新的配置文件啟動四個(gè)新的黃色 worker 子進(jìn)程,此時(shí)是四個(gè)老的綠色 worker 子進(jìn)程和四個(gè)新的黃色的 worker 子進(jìn)程是并存的。那么老的 worker 子進(jìn)程在正常的情況下會在處理已經(jīng)建立好的連接上的請求之后關(guān)閉這個(gè)連接,哪怕這個(gè)連接是 keeplive 請求也會正常關(guān)閉。
但是異常情況,如果有一些請求出現(xiàn)問題,客戶端長時(shí)間無法處理,那么就會導(dǎo)致這個(gè)請求長時(shí)間停留在這個(gè) worker 子進(jìn)程當(dāng)中,那么這個(gè) worker 子進(jìn)程會長時(shí)間存在,因?yàn)樾碌倪B接已經(jīng)跑在黃色的 worker 子進(jìn)程中,所以影響并不會很大,唯一會影響的就是綠色的 worker 子進(jìn)程會長時(shí)間存在,但也只影響已存在的連接,不會影響新的連接。
我們有什么辦法處理呢?在新版本中提供了一個(gè)新的配置 worker_shutdown_timeout,也就是說最長等待多長時(shí)間,這樣 master 進(jìn)程啟動新的黃色 worker 進(jìn)程之后,如果老的 worker 進(jìn)程一直沒有退出,時(shí)間到了之后會強(qiáng)制把老的 worker 進(jìn)程退出掉。
總結(jié)
本文主要講解了 Nginx 平滑升級新的配置文件的流程,在我們了解了優(yōu)雅關(guān)閉 worker 子進(jìn)程和啟動新配置的 worker 子進(jìn)程流程間的關(guān)系后,我們可以更好地處理罕見的異常場景。
歡迎在留言區(qū)留下你的觀點(diǎn),一起討論提高。如果今天的文章讓你有新的啟發(fā),學(xué)習(xí)能力的提升上有新的認(rèn)識,歡迎轉(zhuǎn)發(fā)分享給更多人。