在瞭解Nginx工作原理之前,我們先來瞭解下幾個基本的概念 以及常見的I/O模型。 基本概念 同步:就是指調用方發起一個調用,在沒有得到調用結果之前,該調用不返回。換句話說,也就是調用方發起一個調用後,一直等待被調用方返回結果,直到獲取結果後才執行後續操作。 生活中的同步場景:等電梯: 按電梯方向鍵 ...
在瞭解Nginx工作原理之前,我們先來瞭解下幾個基本的概念 以及常見的I/O模型。
基本概念
-
同步:就是指調用方發起一個調用,在沒有得到調用結果之前,該調用不返回。換句話說,也就是調用方發起一個調用後,一直等待被調用方返回結果,直到獲取結果後才執行後續操作。
生活中的同步場景:等電梯:
- 按電梯方向鍵 --> 用戶發起一個調用
- 電梯不在當前樓層,不做別的事情,繼續等待 --> 一直等待結果
- 電梯到了,開門 --> 獲取到結果
-
非同步:就是指調用方發起一個調用,在沒得到調用結果之前,返回該調用。換句話說,也就說調用方發起一個調用後,不等待被調用方返回結果,繼續執行後續操作。這種情況下,被調用方一般會在處理完調用請求後,通過狀態、通知、或者調用回調函數、介面(由調用方發起調用請求時按要求提供)來通知調用方處理結果。
生活中的非同步場景:使用洗衣機洗衣服:
- 將待洗衣服放進洗衣機
- 用戶按開始運行按鈕 --> 用戶發起一個調用
- 洗衣機開始洗衣服 --> 調用方開始處理請求
- 用戶去做別的事情,比如打掃衛生、煮飯
- 洗衣機洗完衣服,發出鳴叫聲 --> 通知調用方處理結果
-
阻塞:電腦中,主要指進程、線程的因為正在等待某個事件的完成,暫時不具備運行條件而停止運行的一種狀態。也稱為等待狀態。
生活中的阻塞場景:交通堵車
- 道路寬敞,交通順暢,汽車啟動,快速行駛 --> 具備運行條件,運行狀態
- 前方出現堵車,剎車,停止運行,等待交通暢順 --> 失去運行條件,進入阻塞狀態
- 交通順暢,啟動汽車,繼續前行 --> 獲取運行條件,恢復運行狀態。
-
非阻塞:
阻塞的對立面,不多解釋。
將以上幾個概念進行組合,便可得到以下概念:
- 同步阻塞
- 同步非阻塞
- 非同步非阻塞
總結
-
同步和非同步關註的是消息通信機制,關註調用方發起調用後是否主動等待調用結果還是由被動等待被調用方通知。
-
阻塞和非阻塞關註的是調用方在等待調用結果時的狀態。來看下以下這段話可能更好理解。
如果發起一個同步調用, 因為沒有得到結果不返回, 那它毫無疑問就是被"阻塞"了(即調用方處於 “等待” 狀態)。如果調用發出了以後就直接返回了, 毫無疑問, 調用方沒有被“阻塞”。
舉個例子:socket通信。
我們都知道,socket伺服器進程建立監聽後,需要執行
accept
調用,來讀取客戶端建立連接請求,這個就是一個同步調用,此時,如果可以獲取到請求,就會立即返回(同步非阻塞),但是此時,如果獲取不到連接請求,那麼該調用不會返回,進程會一直等待(同步阻塞),
常見I/O模型
基礎知識
內核態/用戶態
操作系統為了保護自己,設計了用戶態、內核態兩個狀態。應用程式一般工作在用戶態,當調用一些底層操作的時候(比如 I/O 操作),就需要切換到內核態才可以進行。
應用程式從網路中接收數據的大致流程
伺服器從網路接收的大致流程如下:
- 數據通過電腦網路來到了網卡
- 把網卡的數據讀取到 socket 緩衝區
- 把 socket 緩衝區讀取到用戶緩衝區,之後應用程式就可以使用了
以兩個應用程式通訊為例,當“A”向"B" 發送一條消息,大致會經過以下流程:
第一步: 應用A把消息發送到 TCP發送緩衝區。
第二步: TCP發送緩衝區再把消息發送出去,經過網路傳遞後,消息會發送到B伺服器的TCP接收緩衝區。
第三步: B再從TCP接收緩衝區去讀取屬於自己的數據。
因為應用之間發送消息是間斷性的,所以,當應用嘗試從TCP緩衝區接收數據時,並不一定能讀取到數據,此外,
TCP緩衝區是有大小限制的,當緩衝區因為寫入速率過快被“填滿”時,也無法繼續寫入數據,這就是為啥會有I/O阻塞的原因之一(除此之外,數據從socket緩衝區拷貝到用戶空間也是需要時間的,即也可能造成阻塞)。
I/O類型劃分
根據是否造成阻塞,可以將I/O類型分成阻塞和非阻塞:
-
阻塞I/O
指在進行 I/O 操作時,如果數據沒有準備好或無法立即讀取/寫入,程式會被阻塞(阻塞調用)等待數據準備好或操作完成,然後才能繼續執行後續的操作。阻塞 I/O 是一種同步 I/O 操作。
-
非阻塞IO
是指在進行 I/O 操作時,如果數據沒有準備好或無法立即讀取/寫入,程式會立即返回,並繼續執行後續的操作。在非阻塞 I/O 中,程式不會等待 I/O 操作的完成,而是立即返回,繼續執行其他任務,然後通過輪詢或選擇函數(如 select、poll、epoll 等)來檢查是否有 I/O 可用。非阻塞 I/O 是一種非同步 I/O 操作。
根據是否同步,可以將I/O類型劃分成同步和非同步:
-
同步IO
它是指程式在進行 I/O 操作時,必須等待 I/O 完成後才能繼續執行後續的操作。
-
非同步I/O
是指程式發起 I/O 請求後進行 I/O 操作時,不需要等待 I/O 操作的完成,繼續執行其他任務,是一種非阻塞的 I/O 操作方式。當 I/O 操作完成後,系統會通知程式,程式再去獲取或處理完成的 I/O 結果。
非同步I/O常見的2種實現方式:
-
callback 回調函數
通過註冊回調函數,在 I/O 操作完成時由操作系統或庫調用該函數處理結果。
-
信號(signal)
通過使用信號機制,在 I/O 操作完成時由操作系統發送相應的信號給進程,進程通過信號處理函數來處理 I/O 完成的事件。
-
接下來,就上圖B應用從TCP緩衝區讀取數據為例,來瞭解下常見的幾種I/O模型。
阻塞型I/O
在應用調用recvfrom
讀取數據時,其系統調用直到數據包到達且被覆制到應用緩衝區中或者發送錯誤時才返回,在此期間一直會等待,即被阻塞
非阻塞型I/O
在應用調用recvfrom
讀取數據時,如果該緩衝區沒有數據的話,系統會直接返回一個EWOULDBLOCK錯誤,不會讓應用一直等待。
在沒有數據的時候會即刻返回錯誤標識,那也意味著如果應用要讀取數據就需要不斷的調用recvfrom
請求,直到讀取到它數據要的數據為止。
復用型I/O
如果在併發的環境下,可能會N個人嚮應用B發送消息,這種情況下我們的應用就必須創建多個線程去讀取數據,每個線程都會自己調用recvfrom
去讀取數據。那麼此時情況可能如下圖:
如上圖,併發情況下伺服器很可能一瞬間會收到幾十上百萬的請求,這種情況下應用B就需要創建幾十上百萬的線程去讀取數據,同時又因為應用線程是不知道什麼時候會有數據讀取,為了保證消息能及時讀取到,那麼這些線程自己必須不斷的向內核發送recvfrom
請求來讀取數據;
那麼問題來了,這麼多的線程不斷調用recvfrom
請求數據,且不說伺服器能不能扛得住這麼多線程,就算扛得住那麼很明顯這種方式是不是太浪費資源了。
所以,有人就提出了一個思路,能不能提供一種方式,可以由一個線程監控多個通信socket(每個socket對應一個文件描述符fd),這樣就可以只需要一個或幾個線程就可以完成數據狀態詢問的操作,當有數據準備就緒之後再分配對應的線程去讀取數據,這麼做就可以節省出大量的線程資源出來,這個就是I/O復用模型的思路,如下圖
進程通過將一個或多個fd傳遞給select
,阻塞在select
操作上,select
幫我們偵測多個fd
是否準備就緒,當有fd
準備就緒時,select
返回數據可讀狀態,應用程式再調用recvfrom
讀取數據。
說明:復用I/O的基本思路就是通過select
或poll
、epoll
來監控多fd
,來達到不必為每個fd
創建一個對應的監控線程,從而減少線程資源創建的目的。
信號驅動型I/O
復用I/O模型解決了一個線程可以監控多個fd
的問題,但是select
是採用輪詢的方式來監控多個fd
的,通過不斷的輪詢fd
的可讀狀態來知道是否有可讀的數據,而無腦的輪詢就顯得有點暴力,因為大部分情況下的輪詢都是無效的,所以有人就想,能不能不要我總是去問你是否數據準備就緒,能不能我發出請求後等你數據準備好了就通知我,所以就衍生了信號驅動I/O模型。
於是信號驅動I/O不是用迴圈請求詢問的方式去監控數據就緒狀態,而是在調用sigaction
時候建立一個SIGIO
的信號聯繫,當內核數據準備好之後再通過SIGIO
信號通知線程數據準備好後的可讀狀態,當線程收到可讀狀態的信號後,此時再向內核發起recvfrom
讀取數據的請求,因為信號驅動I/O的模型下應用線程在發出信號監控後即可返回,不會阻塞,所以這樣的方式下,一個應用線程也可以同時監控多個fd
,如下圖。
首先開啟套介面信號驅動I/O功能,並通過系統調用sigaction
執行一個信號處理函數,此時請求即刻返回,當數據準備就緒時,就生成對應進程的SIGIO
信號,通過信號回調通知應用線程調用recvfrom
來讀取數據
說明:信號驅動 I/O 和多路復用型I/O的主要區別在於:信號驅動型 I/O 的底層實現機制是事件通知,而多路復型用 I/O 的底層實現機制是遍歷+回調。
非同步I/O
不管是I/O復用還是信號驅動,要讀取一個數據總是要發起兩階段的請求,第一次發送select
請求,詢問數據狀態是否準備好,第二次發送recevform
請求讀取數據。
有沒有有一種方式,我只要發送一個請求我告訴內核我要讀取數據,然後我就什麼都不管了,然後內核去幫我去完成剩下的所有事情?
於是有人設計了一種方案,應用只需要向內核發送一個read 請求,告訴內核它要讀取數據後即刻返回;內核收到請求後會建立一個信號聯繫,當數據準備就緒,內核會主動把數據從內核覆制到用戶空間,等所有操作都完成之後,內核會發起一個通知告訴應用,我們稱這種模式為非同步IO模型。
Nginx工作原理
眾所周知,Nginx採用多進程和非同步非阻塞事件驅動模型對外提供服務。
多進程機制
生產環境,Nginx服務實現基本採用的都是多進程模式:一個master進程和N個worker進程(其中N>=1,雖然可以配置為0,但是沒有意義),每個worker進程只包含一個主線程,進程間通過共用記憶體的方式,來共用緩存數據、會話持久性數據等
master 進程並不處理網路請求,主要用於管理worker進程,具體包括以下主要功能:
-
接收來自外界的信號。
-
向各worker進程發送信號。
-
監控woker進程的運行狀態
當worker進程因為出現異常而退出時,自動重新啟動新的worker進程
-
停啟用worker進程。
當woker進程退出後(異常情況下),master會自動啟動新的woker進程。
而worker 進程則負責處理網路請求與響應。每個請求只會在一個 worker 進程中處理,每個worker進程,只能處理自己接收的請求。
Nginx 在啟動時,會先解析配置文件,得到需要監聽的IP地址和埠,然後在 master 進程中創建socket,並綁定要監聽的IP地址和埠,再執行埠監聽,接著fork worker進程(關於fork介紹參見下文),由於worker進程是由master進程fork出來的,所以worker進程將繼承master進程的socket文件描述符。worker進程創建後,都會執行accept
等待並提取全連接隊列中的連接請求。所以,當有連接請求時,所有worker進程都會收到通知,並“爭著”與客戶端建立連接,這就是所謂的“驚群現象”。但是由於只有一個連接請求,所以,同時只會有一個worker進程能成功建立連接,並創建已連接描述符,然後通過已連接描述符來與客戶端通信,讀取請求 -> 解析請求 -> 處理請求 -> 返迴響應給客戶端 ,最後才斷開連接 ,未成功建立連接的請求將再次被掛起。
這就好比你往一群正在睡覺的鴿子中間扔一塊較大的食物,瞬間驚動所有的鴿子,然後所有鴿子都去爭搶那塊食物,最終只有一隻鴿子搶到食物,沒搶到食物的鴿子回去繼續睡覺,等待下一塊食物到來。這樣,每扔一塊食物,都會驚動所有的鴿子,即為驚群。
大量的進程被激活又掛起,顯然,在請求連接數量比較低的情況下,這種方式會比較消耗系統資源。為了避免這種驚群效應,Nginx提供了一個accept_mutex
指令,將設置指令值為on
,可以確保工作進程按序獲取連接。具體實現其實是添加了一個全局互斥鎖,每個工作進程在執行accept
調用之前先去申請鎖,申請到則繼續處理,獲取不到則放棄accept
, 這樣就保證了同一時刻只有一個work進程能accept
連接,避免了驚群問題。
worker進程會競爭監聽客戶端的連接請求:這種方式可能會帶來一個問題,就是可能所有的請求都被一個worker進程給競爭獲取了,導致其他進程都比較空閑,而某一個進程會處於忙碌的狀態,這種狀態可能還會導致無法及時響應連接而丟棄掉本有能力處理的請求。針對這種現象,Nginx使用了一個名為ngx_accept_disabled
的變數控制worker進程是否去競爭獲取accept_mutex鎖。
Nginx代碼(ngx_event_accept.c)實現中,ngx_accept_disabled
被賦值為nginx單個進程的所有連接總數/8,減去剩餘空閑連接數量,其中,單個進程的所有連接總數即為nginx.conf
里配置的worker_connections
值。
ngx_accept_disabled = ngx_cycle->connection_n / 8
- ngx_cycle->free_connection_n;
ngx_event.c
void ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
# 略...
if (ngx_use_accept_mutex) {
if (ngx_accept_disabled > 0) {
ngx_accept_disabled--;
} else {
if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
return;
}
if (ngx_accept_mutex_held) {
flags |= NGX_POST_EVENTS;
} else {
if (timer == NGX_TIMER_INFINITE
|| timer > ngx_accept_mutex_delay)
{
timer = ngx_accept_mutex_delay;
}
}
}
}
# ...略
}
由以上代碼可知,當且僅當剩餘連接數小於總連接數的八分之一時,ngx_accept_disabled
值才大於0。當ngx_accept_disabled
大於0時,不會去獲取accept_mutex
鎖,也就是等於讓出獲取連接的機會,很顯然可以看出,當空閑連接越少時,ngx_accept_disable
越大,於是讓出的機會就越多,這樣其它進程獲取鎖的機會也就越大,就這樣,實現不同worker之間的負載均衡。
註意:Nginx 1.11.3版本之前,accept_mutex
指令預設值為on
,1.11.3版本之後預設為off
,即預設關閉。
支持EPOLLEXCLUSIVE標識的系統(Linux 4.5+,glibc 2.24 )或者listen
指令中使用reuseport選項時,不必開啟accept_mutex
。
EPOLLEXCLUSIVE
說明:
為被附加到目標文件描述符fd的epfd
文件描述符設置獨占喚醒模式。因此,當一個事件發生,並且多個epfd
文件描述符附加到同一個使用EPOLLEXCLUSIVE
的目標文件時,一個或多個epfd
將收到具有epoll_wait(2)
的事件。此場景預設(當未設置EPOLLEXCLUSIVE
時)所有epfd
接收事件。
EPOLLEXCLUSIVE
只能與 EPOLL_CTL_ADD
操作一起指定。
說明:EPOLLEXCLUSIVE
功能支持的實現代碼中也用到了ngx_accept_disabled
變數,如下
ngx_event.c
static void
ngx_reorder_accept_events(ngx_listening_t *ls)
{
ngx_connection_t *c;
/*
* Linux with EPOLLEXCLUSIVE usually notifies only the process which
* was first to add the listening socket to the epoll instance. As
* a result most of the connections are handled by the first worker
* process. To fix this, we re-add the socket periodically, so other
* workers will get a chance to accept connections.
*/
if (!ngx_use_exclusive_accept) {
return;
}
#if (NGX_HAVE_REUSEPORT)
if (ls->reuseport) {
return;
}
#endif
c = ls->connection;
if (c->requests++ % 16 != 0
&& ngx_accept_disabled <= 0)
{
return;
}
if (ngx_del_event(c->read, NGX_READ_EVENT, NGX_DISABLE_EVENT)
== NGX_ERROR)
{
return;
}
if (ngx_add_event(c->read, NGX_READ_EVENT, NGX_EXCLUSIVE_EVENT)
== NGX_ERROR)
{
return;
}
}
#endif
reuseport
配置示例:
http {
server {
listen 80 reuseport
...
}
}
reuseport
選項說明
此參數 (Nginx 1.9.1)指示為每個工作進程創建一個單獨的偵聽套接字(在Linux 3.9+和DragonFly BSD上使用SO_REUSEPORT
套接字選項,或在FreeBSD 12+上使用SO_REUSEPORT_LB
),允許系統內核在工作進程之間分配傳入連接。這目前僅適用於Linux 3.9+、DragonFly BSD和FreeBSD 12+(1.15.1)。
fork簡介
什麼是fork?fork是複製進程的函數,程式開始運行時會產生一個進程,當這個進程(代碼)執行到fork()時,就會通過複製當前進程(包括代碼、數據等)來創建一個新進程,我們稱之為子進程,而當前被覆制的進程我們稱之為父進程,此時父子進程是共存的,他們一起向下執行代碼。除了少部分內容(比如進程ID),子進程和父進程幾乎完全相同,可以做完全相同的事情,當然,如果初始參數或者傳入的變數不同,兩個進程也可以做不同的事。
Nginx進程模型
非同步非阻塞事件驅動模型
事件驅動模型概述
所謂事件驅動就是指在持續事務管理過程中,進行決策的一種策略,即根據當前出現的事件,調動可用資源,執行相關任務,使不斷出現的問題得以解決,防止事務堆積。
在電腦編程領域,事件驅動模型對應一種程式設計方式,該設計的基本結構一般由事件收集器、事件發送器和事件處理器組成。其中,事件收集器專門負責收集所有事件,包括來自用戶的(如滑鼠點擊、鍵盤輸入事件等)、來自硬體的(如時鐘事件等)和來自軟體的(如操作系統、應用程式本身等)。事件發送器負責將收集器收集到的事件分發到目標對象中。事件處理器做具體的事件響應工作。
事件驅動模型一般會採用這種實現思路:設計一個事件迴圈,不斷地檢查目前要處理的事件信息,然後使用事件發送器將待處理事件傳遞給事件處理器進行處理(事件處理器要事先在事件收集器里註冊自己想要處理的事件)。
Nginx中的事件驅動模型
為什麼幾個worker進程能支持高併發的請求呢?這得益於Nginx採用的非同步非阻塞事驅動模型,毫無例外。
對於傳統的web伺服器(比如Apache)而言,其所採用的事件驅動往往局限於TCP連接建立、關閉事件上,一個連接建立以後,在其關閉之前的所有操作都不再是事件驅動,而是退化成順序執行每個操作的批處理模式,這樣每個請求在連接建立後都將始終占用著系統資源,直到關閉才會釋放資源。其事件消費者一般是一個線程、進程。
傳統的web伺服器不同,Nginx將一個請求劃分為多個階段來非同步處理,每個階段只處理請求的一部分,如果請求的這一部分發生阻塞,Nginx不會等待,它會處理其他請求的某一部分。每個階段對應不同的事件,都採用事件驅動的方式進行處理。整個執行過程中,Nginx不會為每個消費事件創建一個進程或線程,其事件消費者只能是某個模塊,僅在被調用時占用當前進程資源,這樣避免了由於創建大量線程、進程帶來的頻繁上下文切換繁,從而避免過高的CPU資源占用,同時也降低了對伺服器其它資源(比如記憶體)的占用。
由於Nginx工作性質決定了每個請求的大部份生命都是在網路傳輸中,實際上花費在伺服器自身的時間片不多,這就是分階段非同步處理請求的情況下,為數不多的進程就能解決高併發的秘密所在。
Nginx支持多種事件驅動模型併在創建worker進程時,初始化對應的事件驅動模型,不指定使用特定模型的情況下,如果平臺支持多種模型,Nginx通常會自動選擇最高效的模型,如果需要,也可以使用use指令顯式指定使用的模型。以下是支持的模型(庫):
-
select
-- 標準方法。Nginx編譯過程中如果沒有其他更效率事件驅動模型庫,它將自動編譯該庫。可以使用--with-select_module
和--without-select_module
兩個參數強制啟用或禁用此模塊的構建。 -
poll
-- 標準方法。Nginx編譯過程中如果沒有其他更效率事件驅動模型庫,它將自動編譯該庫。可以使用--with-poll_module
和--without-poll_module
兩個參數強制啟用或禁用此模塊的構建。 -
kqueue
— 用於FreeBSD 4.1+, OpenBSD 2.9+, NetBSD 2.0, 和macOS的高效方法。 -
epoll
— 用於Linux 2.6+的高效方法1.11.3版本開始,支持
EPOLLRDHUP
(Linux 2.6.17, glibc 2.8) 和EPOLLEXCLUSIVE
(Linux 4.5, glibc 2.24) 標識一些舊發行版本比如SuSE 8.2提供添加epoll支持的補丁到2.4內核。
-
/dev/poll
— 用於Solaris 7 11/99+, HP/UX 11.22+ (eventport), IRIX 6.5.15+, 和Tru64 UNIX 5.1A+的高效方法。 -
eventport
— event port方法用於 Solaris 10+ (推薦使用/dev/poll
替代該方法)。
select
,poll
,epoll
是較為常見的3種模型,其中epoll
最效率,是poll
的增強版,是Nginx的預設事件驅動模型,也是Nginx能支持高併發的關鍵因素。相對於select
、poll
來說,具有以下優點:
-
支持一個進程打開最大文件描述符數量
-
I/O效率不隨文件描述符數量的增加而線性下降
poll
和select
都是創建一個待處理事件列表,然後把這個列表發給內核,返回的時候,再去輪詢檢查這個列表。以判斷這個事件是否發生。在描述符太多的情況下,效率就明顯低下了。而
epoll
則把事件描述符列表的管理交給內核。一旦有某個事件發生,內核將發生事件的事件描述符交給Nginx的進程,而不是將整個事件描述符列表交給進程,讓進程去輪詢具體是哪個描述符。epoll
避免了輪詢整個事件描述符列表,所以效率更高。 -
使用mmap加速內核與用戶空間的消息傳遞
從流程上來講,epoll
模型的使用主要分為三步:
- 創建
epoll
實例的句柄 - 往句柄中添加需要監聽的事件文件描述符
- 等待需要監聽的文件描述符上對應的事件的觸發。
這裡的句柄可以簡單的理解為一個eventpoll
結構體實例,這個結構體中有一個紅黑樹和一個隊列,紅黑樹中主要存儲需要監聽的文件描述符,而隊列則是存儲所監聽的文件描述符中發生的目標事件。
Nginx每個worker進程都維護了一個epoll
句柄,在創建完句柄之後,會通過epoll_ctl()
方法,將監聽socket對應的文件描述符添加到句柄中(對應eventpoll
紅黑樹中的一個節點)。此外,調用epoll_ctl()
方法添加文件描述符之後,會將其與相應的設備(網卡)進行關聯,當設備驅動發生某個事件時,就會回調當前文件描述符的回調方法ep_poll_callback()
,生成一個事件,並且將該事件添加到eventpoll
的事件隊列中。最後,事件迴圈中通過調用epoll_wait()
方法從epoll
句柄中獲取對應的事件(本質就是檢查eventpoll
的事件隊列是否為空,如果有事件則將其返回,否則就會等待事件的發生)。這裡獲取的事件一般都是accept
事件,即接收到客戶端請求,在處理這個事件的時候,會獲取與客戶端通信用的已連接文件描述符,並繼續通過epoll_ctl()
方法將其添加到當前的epoll
句柄中,繼續通過epoll_wait()
方法等待其數據的讀取和寫入事件以接收和發送數據給客戶端。
Nginx熱部署原理
如上圖,master進程充當整個進程組與用戶的交互介面,可以通過向master進程發送信號來實現相關操作。比如執行kill -HUP masterPID
命令,給master進程發送KILL -HUP
信號,告訴Nginx,平滑重啟nginx,要求重啟過程中不能中斷服務。
master進程接收到KILL -HUP
信號後會執行以下操作:
- 重新載入配置文件
- 啟動新的worker進程,並向所有老的worker進程發送信號,告訴他們可以光榮退休了。
- 新的worker在啟動後,就開始接收新的請求,而老的worker進程在收到來自master的信號後,不再接收新的請求,並繼續處理當前進程已接收的請求直至所有請求處理完成,最後退出。
直接給master進程發送信號,這是比較傳統的操作方式,Nginx 0.8本之後,引入了一系列命令行參數,來方便我們管理Nginx。比如,nginx -s reload
,平滑重啟nginx,nginx -s stop
,停止運行Nginx。命令本質也是給master進程發送信號。
KILL -HUP
簡介
在Linux中,Kill 命令用於發送信號給進程,其中SIGHUP
代表Hangup,在UNIX系統中,這個信號通常被用來重新讀配置文件,而對於一些進程而言,重新讀取配置文件之後,它們會嘗試將自己的行為變為預設行為,而不需要重啟或重載。
Kill -HUP
,簡單理解就是發送給進程的一種信號,它能夠讓進程重讀它的配置文件並且重新打開它的日誌文件。在發送Kill -HUP
信號時,操作系統會把SIGHUP
信號發送到進程表,然後內核會從內核級別的進程表中查找那些進程已經註冊了SIGHUP
信號。如果它們對SIGHUP
信號進行了處理或者忽略了該信號,則不做處理,否則內核就會把SIGHUP
信號發送給進程。
當某個進程接收到SIGHUP信號
後,它會根據自己的處理方式來處理該信號,通常包括:如果進程正在運行,則進程暫停,然後運行信號處理程式;如果進程處於休眠狀態,則先喚醒進程,再運行信號處理程式
作者:授客
微信/QQ:1033553122
全國軟體測試QQ交流群:7156436
Git地址:https://gitee.com/ishouke
友情提示:限於時間倉促,文中可能存在錯誤,歡迎指正、評論!
作者五行缺錢,如果覺得文章對您有幫助,請掃描下邊的二維碼打賞作者,金額隨意,您的支持將是我繼續創作的源動力,打賞後如有任何疑問,請聯繫我!!!
微信打賞
支付寶打賞 全國軟體測試交流QQ群