nginx可以在不停止服務的情況下,升級更新自己的bin文件,那這些是怎麼實現的呢,讓我們看一下nginx的源碼 ...
一、平滑升級步驟
1、重命名之前的sbin/nginx文件,將新的nginx文件放到sbin/目錄下
#mv ./sbin/nginx ./sbin/nginx.old
#cp ~/nginx ./sbin/
2、向正在運行的nginx發送USR2信號啟動新的nginx,這個時候新老nginx都會接收請求,看那一個進程能搶到鎖,搶到鎖的worker進程可以accpet新請求
#kill -USR2 `cat nginx.pid`
3、觀察新的nginx運行無誤後,向舊nginx發信號 停止舊nginx的運行
#kill -QUIT `cat nginx.pid.oldbin`
二、源碼分析
1,nginx啟動時 設置信號監聽函數,監聽信號
src/core/nginx.c
368行 ngx_init_signals函數 設置要監聽的信號,和信號的處理函數
src/core/nginx.c
291-293行 信號為sig->signo,對應的處理函數為sig->handler
sig的定義如下
QUIT和USR2的信號處理函數都為 ngx_signal_handler
對應的流程圖為
2、master進程通過sigsuspend掛起在信號監聽處
3,向master進程id發送USR2信號
ngx_signal_handler處理USR2信號
src/os/unix/ngx_process.c
372行 設置了ngx_change_binary=1
master進程接收到信號,從掛起狀態恢復,繼續執行
src/os/unix/ngx_process.c
277行 ngx_exec_new_binary通過fork啟動新的nginx bin文件
src/core/nginx.c
589行 ngx_set_evviroment 設置新nginx bin的環境變數
640行 ngx_rename_file 通過rename函數將nginx.pid文件重命名為nginx.pid.oldbin
651行 ngx_execute 啟動新的bin文件
由於nginx老master進程fork出的新nginxmaster進程,他們可以監聽同一個埠,所以新nginx和老nginx可以同時監聽埠,具體誰執行看哪一個worker子進程搶到了鎖,可以accpet新連接
src/os/unix/ngx_process.c
src/os/unix/ngx_process.c
src/os/unix/ngx_process.c
對應的流程圖如下
4、向老的nginx進程發送QUIT信號,從容關閉
master進程收到QUIT信號後,將ngx_quit置為1
master進程接收到信號,從掛起狀態恢復,繼續執行
209行 ngx_signal_worket_processes 向worker進程發送 NGX_SHUTDOWN_SIGNAL(QUIT)信號
215行 ngx_close_socket 主進程關閉監聽的socket
src/os/unix/ngx_process_cycle.c
504行 通過kill函數向所有worker進程發送信號
5、worker進程收到NGX_SHUTDOWN_SIGNAL(QUIT)信號
src/os/unix/ngx_process.c
360行 worker進程將ngx_quit置為1
worker進程收到信號後從epoll_wait中喚醒從ngx_process_events_and_timers函數中恢復,
710-714行 發現ngx_quit=1後將ngx_quit恢復為0,ngx_exiting置為1,
713行 通過ngx_close_listening_sockets關閉處理的socket
609行 下一次迴圈發現ngx_exiting=1後,處理隊列中的已有事件和超時事件,發現沒有要處理的事件了,就通過ngx_worker_process_exit退出worker進程
src/os/unix/ngx_process_cycle.c
1024行 調用各個模塊的exit_process方法
1067行 銷毀記憶體池
對應的流程圖如下
6、子進程退出後,作為父進程的master進程會收到SIGCHLD信號
src/os/unix/ngx_process.c
387行 父進程收到SIGCHLD後將ngx_reap置為1,
437行 發現信號是SIGCHLD後執行ngx_process_get_status函數判斷worker子進程是正常退出,還是異常退出
src/os/unix/ngx_process.c
494-499行 如果發現worker子進程如果是正常退出的,會將exited置為1
master進程接收到信號,從掛起狀態恢復,繼續執行
176行 發現ngx_reap=1後,ngx_reap_children函數判斷是否需要重啟worker進程
如果worker是因為收到了quit信號正常退出的,所有worker進程退出時,live=0
183行 live=0 並且收到了ngx_quit信號 通過ngx_master_process_exit關閉master進程
src/os/unix/ngx_process_cycle.c
619 如果worker至今才是因為意外退出的,並且可以重啟,則調用ngx_spawn_process重新啟動一個worker子進程
642 如果有worker進程還在運行則live=1 如果全部的worker子進程都已經退出則live=0
src/os/unix/ngx_process_cycle.c
656行 ngx_delete_pidfile 刪除pid文件
666行 ngx_close_listening_sockets 關閉監聽埠
685行 銷毀記憶體池
686行 退出
對應的流程圖如下
PS:推薦一個好朋友的微信公眾號,一個每天都在思考或者在思考路上的公眾號運營少女~