一、背景
在服務器開發過程中,難免需要重啟服務加載新的代碼或配置,如果能夠保證server重啟的過程中服務不間斷,那重啟對于業務的影響可以降為0。最近調研了一下nginx平滑重啟,覺得很有意思,記錄下來供有興趣的同學查閱。
二、重啟流程
重啟意味著新舊接替,在交接任務的過程中勢必會存在新舊server并存的情形,因此,重啟的流程大致為: 啟動新的server 新舊server并存,兩者共同處理請求,提供服務 舊的server處理完所有的請求之后優雅退出 這里,最主要的問題在于如何保證新舊server可以并存,如果重啟前后的server端口一致,如何保證兩者可以監聽同一端口。三、nginx實現
為了驗證nginx平滑重啟,筆者首先嘗試nginx啟動的情形下再次開啟一個新的server實例,結果如圖:
很明顯,重新開啟server實例是行不通的,原因在于新舊server使用了同一個端口80,在未開始socket reuseport選項復用端口時,bind系統調用會出錯。nginx默認bind重試5次,失敗后直接退出。而nginx需要監聽IPV4地址0.0.0.0和IPV6地址[::],故圖中打印出10條emerg日志。
接下來就開始嘗試平滑重啟命令了,一共兩條命令:
kill -USR2 `cat /var/run/nginx.pid`kill -QUIT `cat /var/run/nginx.pid.oldbin`
第一條命令是發送信號USR2給舊的master進程,進程的pid存放在/var/run/nginx.pid文件中,其中nginx.pid文件路徑由nginx.conf配置。
第二條命令是發送信號QUIT給舊的master進程,進程的pid存放在/var/run/nginx.pid.oldbin文件中,隨后舊的master進程退出。
那么問題來了,為什么舊的master進程的pid存在于兩個pid文件之中?事實上,在發送信號USR2給舊的master進程之后,舊的master進程將pid重命名,原先的nginx.pid文件rename成nginx.pid.oldbin。這樣新的master進行就可以使用nginx.pid這個文件名了。
先執行第一條命令,結果如圖:
不錯,新舊master和worker進程并存了。 再來第二條命令,結果如圖:
如你所見,舊的master進程8527和其worker進程全部退出,只剩下新的master進程12740。
不由得產生困惑,為什么手動開啟一個新的實例行不通,使用信號重啟就可以達到。先看下nginx log文件:
除了之前的錯誤日志,還多了一條notice,意思就是繼承了sockets,fd值為6,7。 隨著日志翻看nginx源碼,定位到nginx.c/ngx_exec_new_binary函數之中,
新聞熱點
疑難解答