Nginx 工作原理簡介

来源:https://www.cnblogs.com/shouke/archive/2023/12/17/17908653.html
-Advertisement-
Play Games

在瞭解Nginx工作原理之前,我們先來瞭解下幾個基本的概念 以及常見的I/O模型。 基本概念 同步:就是指調用方發起一個調用,在沒有得到調用結果之前,該調用不返回。換句話說,也就是調用方發起一個調用後,一直等待被調用方返回結果,直到獲取結果後才執行後續操作。 生活中的同步場景:等電梯: 按電梯方向鍵 ...


在瞭解Nginx工作原理之前,我們先來瞭解下幾個基本的概念 以及常見的I/O模型。

基本概念

  • 同步:就是指調用方發起一個調用,在沒有得到調用結果之前,該調用不返回。換句話說,也就是調用方發起一個調用後,一直等待被調用方返回結果,直到獲取結果後才執行後續操作。

    生活中的同步場景:等電梯:

    1. 按電梯方向鍵 --> 用戶發起一個調用
    2. 電梯不在當前樓層,不做別的事情,繼續等待 --> 一直等待結果
    3. 電梯到了,開門 --> 獲取到結果
  • 非同步:就是指調用方發起一個調用,在沒得到調用結果之前,返回該調用。換句話說,也就說調用方發起一個調用後,不等待被調用方返回結果,繼續執行後續操作。這種情況下,被調用方一般會在處理完調用請求後,通過狀態、通知、或者調用回調函數、介面(由調用方發起調用請求時按要求提供)來通知調用方處理結果。

    生活中的非同步場景:使用洗衣機洗衣服:

    1. 將待洗衣服放進洗衣機
    2. 用戶按開始運行按鈕 --> 用戶發起一個調用
    3. 洗衣機開始洗衣服 --> 調用方開始處理請求
    4. 用戶去做別的事情,比如打掃衛生、煮飯
    5. 洗衣機洗完衣服,發出鳴叫聲 --> 通知調用方處理結果
  • 阻塞:電腦中,主要指進程、線程的因為正在等待某個事件的完成,暫時不具備運行條件而停止運行的一種狀態。也稱為等待狀態。

    生活中的阻塞場景:交通堵車

    1. 道路寬敞,交通順暢,汽車啟動,快速行駛 --> 具備運行條件,運行狀態
    2. 前方出現堵車,剎車,停止運行,等待交通暢順 --> 失去運行條件,進入阻塞狀態
    3. 交通順暢,啟動汽車,繼續前行 --> 獲取運行條件,恢復運行狀態。
  • 非阻塞

    阻塞的對立面,不多解釋。

將以上幾個概念進行組合,便可得到以下概念:

  • 同步阻塞
  • 同步非阻塞
  • 非同步非阻塞

總結

  • 同步和非同步關註的是消息通信機制,關註調用方發起調用後是否主動等待調用結果還是由被動等待被調用方通知。

  • 阻塞和非阻塞關註的是調用方在等待調用結果時的狀態。來看下以下這段話可能更好理解。

    如果發起一個同步調用, 因為沒有得到結果不返回, 那它毫無疑問就是被"阻塞"了(即調用方處於 “等待” 狀態)。如果調用發出了以後就直接返回了, 毫無疑問, 調用方沒有被“阻塞”。

    舉個例子:socket通信。

    我們都知道,socket伺服器進程建立監聽後,需要執行accept調用,來讀取客戶端建立連接請求,這個就是一個同步調用,此時,如果可以獲取到請求,就會立即返回(同步非阻塞),但是此時,如果獲取不到連接請求,那麼該調用不會返回,進程會一直等待(同步阻塞),

常見I/O模型

基礎知識

內核態/用戶態

操作系統為了保護自己,設計了用戶態、內核態兩個狀態。應用程式一般工作在用戶態,當調用一些底層操作的時候(比如 I/O 操作),就需要切換到內核態才可以進行。

應用程式從網路中接收數據的大致流程

伺服器從網路接收的大致流程如下:

  1. 數據通過電腦網路來到了網卡
  2. 把網卡的數據讀取到 socket 緩衝區
  3. 把 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種實現方式

    1. callback 回調函數

      通過註冊回調函數,在 I/O 操作完成時由操作系統或庫調用該函數處理結果。

    2. 信號(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的基本思路就是通過selectpollepoll 來監控多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進程,具體包括以下主要功能:

  1. 接收來自外界的信號。

  2. 向各worker進程發送信號。

  3. 監控woker進程的運行狀態

    當worker進程因為出現異常而退出時,自動重新啟動新的worker進程

  4. 停啟用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 替代該方法)。

selectpollepoll是較為常見的3種模型,其中epoll最效率,是poll的增強版,是Nginx的預設事件驅動模型,也是Nginx能支持高併發的關鍵因素。相對於selectpoll來說,具有以下優點:

  1. 支持一個進程打開最大文件描述符數量

  2. I/O效率不隨文件描述符數量的增加而線性下降

    pollselect都是創建一個待處理事件列表,然後把這個列表發給內核,返回的時候,再去輪詢檢查這個列表。以判斷這個事件是否發生。在描述符太多的情況下,效率就明顯低下了。

    epoll則把事件描述符列表的管理交給內核。一旦有某個事件發生,內核將發生事件的事件描述符交給Nginx的進程,而不是將整個事件描述符列表交給進程,讓進程去輪詢具體是哪個描述符。epoll避免了輪詢整個事件描述符列表,所以效率更高。

  3. 使用mmap加速內核與用戶空間的消息傳遞

從流程上來講,epoll模型的使用主要分為三步:

  1. 創建epoll實例的句柄
  2. 往句柄中添加需要監聽的事件文件描述符
  3. 等待需要監聽的文件描述符上對應的事件的觸發。

這裡的句柄可以簡單的理解為一個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信號後會執行以下操作:

  1. 重新載入配置文件
  2. 啟動新的worker進程,並向所有老的worker進程發送信號,告訴他們可以光榮退休了。
  3. 新的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群  
              


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ProgressBar(進度條)是在Qt中常用的用戶界面組件之一,用於顯示任務的完成進度。它通常以一個水平或垂直的條形圖形式展示,表示任務已完成的比例。進度條組件提供了一種直觀的方式來顯示任務的進度,讓用戶清晰地瞭解任務的完成情況。其還可根據需要在水平或垂直方向上顯示,以適應不同的界面佈局。 ...
  • 日常業務開發過程中,可能第三方的伺服器分佈在世界的各個角落,所以請求三方介面的時候,難免會遇到一些網路問題,這時候需要加入重試機制了,這期就給大家分享幾個介面重試的寫法。 重試機制實現 1. 迴圈重試 這是最簡單也最直接的一種方式。在請求介面的代碼塊中加入迴圈,如果請求失敗則繼續請求,直到請求成功或 ...
  • 數據的預處理是數據分析,或者機器學習訓練前的重要步驟。通過數據預處理,可以 提高數據質量,處理數據的缺失值、異常值和重覆值等問題,增加數據的準確性和可靠性 整合不同數據,數據的來源和結構可能多種多樣,分析和訓練前要整合成一個數據集 提高數據性能,對數據的值進行變換,規約等(比如無量綱化),讓演算法更加 ...
  • 為了實現一個包含靜態文件輸出、GET、POST 請求處理(含參數讀取)、文件上傳和下載功能的 Web API 服務,我們將使用 cpp-httplib 作為 HTTP 伺服器庫。首先,確保你已經安裝了該庫。 git clone https://github.com/yhirose/cpp-httpl ...
  • 獲取PDF文件中文字或圖片的坐標可以實現精確定位,這對於快速提取指定區域的元素,以及在PDF中添加註釋、標記或自動蓋章等操作非常有用。本文將詳解如何使用國產PDF庫通過C# 提取PDF中指定文本或圖片的坐標位置(X, Y軸)。 ✍ 用於操作PDF文件的第三方庫為Spire.PDF for .NET。 ...
  • 在我們開發項目的Web API的時候,隨著項目功能要求越來越多,可能我們會為控制器基類增加越來越多的基礎功能,有些功能有一定的適應性,但可能在一般的子類中用不到,而隨著對控制器控制要求越來越精細,那麼需要為基類或者子類增加更多的控制功能,這樣隨著迭代的進行,有些控制器的功能會顯得越來越笨重。這個時候... ...
  • 在C#語言中,LINQ是一種強大的查詢語言,用於在.NET應用程式中對各種數據源執行查詢操作。其中,Range和Repeat是兩個在LINQ中常用的方法,它們分別用於生成一系列連續的數字和重覆指定的元素。有時候會把這兩個方法混淆,本文將深入探討這兩個方法的用法和應用場景。 1、Range方法 Ran ...
  • 有時候,為了追求便利性,我們可能會讓前端直接將文件上傳到阿裡雲OSS,然後將URL提交給ASP.NET。然而,這種做法意味著前端需要擁有OSS的訪問密鑰,而將密鑰存放在前端,無疑增加了被破解的風險。因此,最安全的做法仍然是由伺服器端負責上傳文件到OSS。 接下來,我將演示如何實現分塊上傳到伺服器的過 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...