1. 程式人生 > >nginx 事件機制原理

nginx 事件機制原理

取消 線性 程序設計 狀態 請求 方案 www 是否 產生

事件驅動模型是Nginx服務器保障完整功能和具有良好性能的重要機制之一。

事件驅動模型概述

實際上,事件驅動並不是計算機編程領域的專業詞匯,它是一種比較古老的響應事件的模型,在計算機編程、公共關系、經濟活動等領域均有很廣泛的應用。顧名思義,事件驅動就是在持續事務管理過程中,由當前時間點上出現的事件引發的調動可用資源執行相關任務,解決不斷出現的問題,防止事務堆積的一種策略。在計算機編程領域,事件驅動模型對應一種程序設計方式,Event-driven programming,即事件驅動程序設計。
事件驅動模型一般是由事件收集器事件發送器事件處理器三部分基本單元組成。
其中, 事件收集器

專門負責收集所有的事件,包括來自用戶的(如鼠標單擊事件、鍵盤輸入事件等)、來自硬件的(如時鐘事件等)和來自軟件的(如操作系統、應用程序本身等)。
事件發送器負責將收集器收集到的事件分發到目標對象中。目標對象就是事件處理器所處的位置。事件處理器主要負責具體事件的響應工作,它往往要到實現階段才完全確定。
在程序設計過程中,對事件驅動機制的實現方式有多種,這裏介紹batch programming,即批次程序設計。批次的程序設計是一種比較初級的程序設計方式。使用批次程序設計的軟件,其流程是由程序設計師在實際編碼過程中決定的,也就是說,在程序運行的過程中,事件的發生、事件的發送和事件的處理都是預先設計好的。由此可見,事件驅動程序設計更多的關註了事件產生的隨機性,使得應用程序能夠具備相當的柔性,可以應付種種來自用戶、硬件和系統的離散隨機事件,這在很大程度上增強了用戶和軟件的交互性和用戶操作的靈活性。
事件驅動程序可以由任何編程語言來實現,只是難易程度有別。如果一個系統是以事件驅動程序模型作為編程基礎的,那麽,它的架構基本上是這樣的:預先設計一個事件循環所形成的程序,這個事件循環程序構成了“事件收集器”,它不斷地檢查目前要處理的事件信息,然後使用“事件發送器”傳遞給“事件處理器”。“事件處理器”一般運用虛函數機制來實現。

Nginx中的事件驅動模型

Nginx服務器響應和處理Web請求的過程,就是基於事件驅動模型的,它也包含事件收集器、事件發送器和事件處理器等三部分基本單元。Nginx的“事件收集器”和“事件發送器”的實現沒有太大的特點,重點介紹一下它的“事件處理器”。

通常,我們在編寫服務器處理模型的程序時,基於事件驅動模型,“目標對象”中的“事件處理器”可以有以下幾種實現辦法:

  • “事件發送器”每傳遞過來一個請求,“目標對象”就創建一個新的進程,調用“事件處理器”來處理該請求。
  • “事件發送器”每傳遞過來一個請求,“目標對象”就創建一個新的線程,調用“事件處理器”來處理該請求。
  • “事件發送器”每傳遞過來一個請求,“目標對象”就將其放入一個待處理事件的列表,使用非阻塞I/O方式調用“事件處理器”來處理該請求。

以上的三種處理方式,各有特點,第一種方式,由於創建新的進程的開銷比較大,會導致服務器性能比較差,但其實現相對來說比較簡單。
第二種方式,由於要涉及到線程的同步,故可能會面臨死鎖、同步等一系列問題,編碼比較復雜。
第三種方式,在編寫程序代碼時,邏輯比前面兩種都復雜。大多數網絡服務器采用了第三種方式,逐漸形成了所謂的“事件驅動處理庫”。
事件驅動處理庫又被稱為多路IO復用方法,最常見的包括以下三種:select模型,poll模型和epoll模型。Nginx服務器還支持rtsig模型、kqueue模型、dev/poll模型和eventport模型等。通過Nginx配置可以使得Nginx服務器支持這幾種事件驅動處理模型。這裏詳細介紹以下它們。

select庫

select庫,是各個版本的Linux和Windows平臺都支持的基本事件驅動模型庫,並且在接口的定義上也基本相同,只是部分參數的含義略有差異。使用select庫的步驟一般是:
首先,創建所關註事件的描述符集合。對於一個描述符,可以關註其上面的(Read)事件、寫(Write)事件以及異常發送(Exception)事件,所以要創建三類事件描述符集合,分別用來收集讀事件的描述符、寫事件的描述符和異常事件的描述符。
其次,調用底層提供的select()函數,等待事件發生。這裏需要註意的一點是,select的阻塞與是否設置非阻塞I/O是沒有關系的。
然後,輪詢所有事件描述符集合中的每一個事件描述符,檢查是否有相應的事件發生,如果有,就進行處理。
Nginx服務器在編譯過程中如果沒有為其指定其他高性能事件驅動模型庫,它將自動編譯該庫。我們可以使用--with-select_module和--without-select_module兩個參數強制Nginx是否編譯該庫。

poll庫

poll庫,作為Linux平臺上的基本事件驅動模型,實在Linux2.1.23中引入的。Windows平臺不支持poll庫。
poll與select的基本工作方式是相同的,都是現創建一個關註事件的描述符集合,再去等待這些事件發生,然後在輪詢描述符集合,檢查有沒有事件發生,如果有,就進行處理。
poll庫與select庫的主要區別在於,select庫需要為讀事件、寫事件和異常事件分別創建一個描述符集合,因此在最後輪詢的時候,需要分別輪詢這三個集合。而poll庫只需要創建一個集合,在每個描述符對應的結構上分別設置讀事件、寫事件或者異常事件,最後輪詢的時候,可以同時檢查這三種事件是否發生。可以說,poll庫是select庫的優化實現。
Nginx服務器在編譯過程中如果沒有為其制定其他高性能事件驅動模型庫,它將自動編譯該庫。我們可以使用--with-poll_module和--without-poll_module兩個參數強制Nginx是否編譯該庫。

epoll庫

epoll庫是Nginx服務器支持的高性能事件驅動庫之一,它是公認的非常優秀的事件驅動模型,和poll庫及select庫有很大的不同。epoll屬於poll庫的一個變種,是在Linux 2.5.44中引入的,在Linux 2.6以上的版本都可以使用它。poll庫和select庫在實際工作中,最大的區別在於效率。
從前面的介紹我們知道,它們的處理方式都是創建一個待處理事件列表,然後把這個列表發給內核,返回的時候,再去輪詢檢查這個列表,以判斷事件是否發生。這樣在描述符比較多的應用中,效率就顯得比較低下了。一種比較好的做法是,把描述符列表的管理交給內核負責,一旦有某種事件發生,內核把發生事件的描述符列表通知給進程,這樣就避免了輪詢整個描述符列表。epoll庫就是這樣一種模型。
首先,epoll庫通過相關調用通知內核創建一個由N個描述符的事件列表。然後,給這些描述符設置所關註的事件,並把它添加到內核的事件列表中去,在具體的編碼過程中也可以通過相關調用對事件列表中的描述符進行修改和刪除。
完成設置之後,epoll庫就開始等待內核通知事件發生了。某一事件發生後,內核將發生事件的描述符列表上報給epoll庫。得到事件列表的epoll庫,就可以進行事件處理了。
epoll庫在Linux平臺上是最高效的。它支持一個進程打開大數目的事件描述符,上限是系統可以打開文件的最大數目。同時,epoll庫的IO效率不隨描述符數目增加而線性下降,因為它只會對內核上報的“活躍”的描述符進行操作。

rtsig模型

rtsig是Real-Time Signal的縮寫,是實時信號的意思。從嚴格意義上說,rtsig模型並不是常用的事件驅動模型,但Nginx服務器使用了使用實時信號對事件進行響應的支持,官方文檔中將rtsig模型與其他的事件驅動模型並列。
使用rtsig模型時,工作進程會通過系統內核建立一個rtsig隊列用於存放標記事件發生(在Nginx服務器應用中特指客戶端請求發生)的信號。每個事件發生時,系統內核就會產生一個信號存放到rtsig隊列中等待工作進程的處理。
需要指出的是,rtsig隊列有長度限制,超過該長度後就會發生溢出。默認情況下,Linux系統事件信號隊列的最大長度設置為1024,也就是同時最多可以存放1024個發生事件的信號。在Linux 2.6.6-mm2之前的版本中,系統各個進程的事件信號隊列是由內核統一管理的,用戶可以通過修改內核參數/proc/sys/kernel/rtsig-max/來自定義該長度設置。在Linux 2.6.6-mm2之後的版本中,該內核參數被取消,系統各個進程分別擁有各自的事件信號隊列,這個隊列的大小由Linux系統的RLIMT_SIGPENGIND參數定義,在執行setrlimit()系統調用時確定該大小。Nginx提供了worker_rlimit_sigpending參數用於調節這種情況下的事件信號隊列長度。
當rtsig隊列發生溢出時,Nginx將暫時停止使用rtsig模型,而調用poll庫處理未處理的事件,直到rgsit信號隊列全部清空,然後再次啟動rtsig模型,以防止新的溢出發生。
Nginx在配置文件中提供了相關參數對rtsig模型的使用配置。編譯Nginx服務器時,使用--with-rtsig_module配置選項來啟用rtsig模型的編譯。

其他事件驅動模型

除了以上四種主要的事件驅動模型,Nginx服務器針對特定的Linux平臺提供了響應的事件驅動模型支持。目前實現的主要有kqueue模型、/dev/poll模型和eventport模型等。

  • kqueue模型,是用於支持BSD系列平臺的高效事件驅動模型,主要用在FreeBSD 4.1及以上版本、OpenBSD 2.9及以上版本、NetBSD 2.0及以上版本以及Mac OS X平臺上。該模型也是poll庫的一個變種,其和epoll庫的處理方式沒有本質上的區別,都是通過避免輪詢操作提供效率。該模型同時支持條件觸發(level-triggered,也叫水平觸發,只要滿足條件就觸發一個事件)和邊緣觸發(edge-triggered,每當狀態變化時,觸發一個事件)。如果大家在這些平臺下使用Nginx服務器,建議選在該模型用於請求處理,以提高Nginx服務器的處理性能。
  • /dev/poll模型,適用於支持Unix衍生平臺的高效事件驅動模型,其主要在Solaris711/99及以上版本、HP/UX 11.22及以上版本、IRIX 6.5.15及以上版本和Tru64 UNIX 5.1A及以上版本的平臺中使用。該模型是Sun公司在開發Solaris系列平臺時提出的用於完成事件驅動機制的方案,它使用了虛擬的/dev/poll設備,開發人員可以將要監視的文件描述符加入這個設備,然後通過ioctl()調用來獲取事件通知。在以上提到的平臺中,建議使用該模型處理請求。
  • eventport模型,適用於支持Solaris 10及以上版本平臺的高效事件驅動模型。該模型也是Sun公司在開發Solaris系列平臺時提出的用於完成事件驅動機制的方案,它可以有效防止內核崩潰情況的發生。Nginx服務器為此提供了支持。

以上就是Nginx服務器支持的事件驅動庫。可以看到,Nginx服務器針對不同的Linux或Unix衍生平臺提供了多種事件驅動模型的處理,盡量發揮系統平臺本身的優勢,最大程度地提高處理客戶端請求事件的能力。在實際工作中,我們需要根據具體情況和應用情景選擇使用不同的事件驅動模型,以保證Nginx服務器的高效運行。



作者:吃瓜的東
鏈接:https://www.jianshu.com/p/2b95794a3654
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權並註明出處。

nginx 事件機制原理