今天這篇文章主要來介紹下 Nginx 的 reload 流程。實際上在之前文章中,在更改了 nginx 配置檔案時,我們都會執行 nginx -s reload 命令,我們執行這條命令的原因是希望 nginx 不停止服務始終在處理新的請求的同時把 nginx 的配置檔案平滑的把舊的 nginx.conf 配置更新為新的 nginx.conf 配置。
這樣一個功能對於 nginx 非常有必要,但是有時候我們會發現在執行 nginx -s reload
命令後,worker 子程式的數量會變多了,這是因為老的配置執行的 worker 程式長時間沒有退出,當使用 stream 做四層反向代理的時候,可能這種場景會更多。
那麼下面我們通過分析 nginx 的 reload 流程,來探究下 nginx 到底做了些什麼?所謂優雅的退出和立即退出有什麼區別?
第一步在修改好 nginx 的配置檔案 nginx.conf 後,向 master 程式傳送 HUP 訊號,這實際上和我們在命令列執行 nginx -s reload
命令效果是一樣的。
那麼 master 程式在收到 HUP 訊號以後,會在第二步檢查我們的配置檔案語法是否正確,也就是說我們並不一定非要在 nginx -s reload 前執行 nginx -t 檢驗下語法是否正確,因為在第二步 nginx 的 master 程式一定會執行這個步驟。
在 nginx 的配置語法全部正確以後,master 程式會開啟新的監聽埠,為什麼要在 master 程式中開啟新的監聽埠?因為我們可能在 nginx.conf 中會引入新的例如 443 或者之前我們沒有開啟的的監聽埠,而所有 worker 程式是 master 程式 的子程式,子程式會繼承父程式所有已經開啟的埠,這是 linux 作業系統定義的,所以第三步,我們 master 程式開啟了可能引入的新的監聽埠。
接下來 mster 程式會用新的 nginx.conf 配置檔案來啟動新的 worker 子程式,那麼老的 worker 子程式會怎麼樣呢?
我們會在第五步在啟動新的 worker 子程式以後,由 master 程式再向老 worker 子程式傳送 QUIT 訊號,QUIT 訊號和 TERM,INT 訊號是不一樣的,QUIT 訊號是請優雅地關閉子程式,這時候需要關注順序,因為 nginx 需要保證平滑,所以要先啟動新的 worker 子程式,再向老的 worker 子程式傳送 QUIT 訊號。
那麼老的 master 子程式收到 QUIT 訊號後,首先關閉監聽控制程式碼,也就是說這個時候新的連線只會到新的 worker 子程式,所以雖然他們之間有時間差,但是時間是非常快速的,那麼關閉監聽控制程式碼後,處理完當前連線後就結束程式。
下面看 reload 不停機載入新配置的圖示:
master 程式上原先有四個綠色的 worker 子程式,它們使用了老的配置,當我們更改了 nginx.conf 配置檔案後,向 master 傳送 SIGHUP 訊號或者執行 reload 命令, 然後 master 會用新的配置檔案啟動四個新的黃色 worker 子程式,此時是四個老的綠色 worker 子程式和四個新的黃色的 worker 子程式是並存的。那麼老的 worker 子程式在正常的情況下會在處理已經建立好的連線上的請求之後關閉這個連線,哪怕這個連線是 keeplive 請求也會正常關閉。
但是異常情況,如果有一些請求出現問題,客戶端長時間無法處理,那麼就會導致這個請求長時間停留在這個 worker 子程式當中,那麼這個 worker 子程式會長時間存在,因為新的連線已經跑在黃色的 worker 子程式中,所以影響並不會很大,唯一會影響的就是綠色的 worker 子程式會長時間存在,但也隻影響已存在的連線,不會影響新的連線。
我們有什麼辦法處理呢?在新版本中提供了一個新的配置 worker_shutdown_timeout,也就是說最長等待多長時間,這樣 master 程式啟動新的黃色 worker 程式之後,如果老的 worker 程式一直沒有退出,時間到了之後會強制把老的 worker 程式退出掉。
本文主要講解了 Nginx 平滑升級新的配置檔案的流程,在我們瞭解了優雅關閉 worker 子程式和啟動新配置的 worker 子程式流程間的關係後,我們可以更好地處理罕見的異常場景。
摘自:武培軒 - 作者武培軒