最近在讀 Nginx 相關的書籍,作一下讀書筆記。web
Nginx 做爲業界知名的高性能服務器,被普遍的應用。它的高性能正是因爲其優秀的架構設計,其架構主要包括這幾點:模塊化設計、事件驅動架構、請求的多階段異步處理、管理進程與多工做進程設計、內存池的設計,如下內容依次進行說明。服務器
高度模塊化的設計是 Nginx 的架構基礎。在 Nginx 中,除了少許的核心代碼,其餘一切皆爲模塊。網絡
全部模塊間是分層次、分類別的,Nginx 官方共有五大類型的模塊:核心模塊、配置模塊、事件模塊、HTTP 模塊、mail 模塊。它們之間的關係以下:架構
在這 5 種模塊中,配置模塊和核心模塊是與 Nginx 框架密切相關的。而事件模塊則是 HTTP 模塊和 mail 模塊的基礎。HTTP 模塊和 mail 模塊的「地位」相似,它們都是更關注於應用層面。併發
事件驅動架構,簡單的說就是由一些事件發生源來產生事件,由事件收集器來收集、分發事件,而後由事件處理器來處理這些事件(事件處理器須要先在事件收集器裏註冊本身想處理的事件)。負載均衡
對於 Nginx 服務器而言,通常由網卡、磁盤產生事件,Nginx 中的事件模塊將負責事件的收集、分發操做;而全部的模塊均可能是事件消費者,它們首先須要向事件模塊註冊感興趣的事件類型,這樣,在有事件產生時,事件模塊會把事件分發到相應的模塊中進行處理。框架
對於傳統 web 服務器(如 Apache)而言,採用的所謂事件驅動每每侷限在 TCP 鏈接創建、關閉事件上,一個鏈接創建之後,在其關閉以前的全部操做都再也不是事件驅動,這時會退化成按順序執行每一個操做的批處理模式,這樣每一個請求在鏈接創建後都將始終佔用着系統資源,直到關閉纔會釋放資源。這種請求佔用着服務器資源等待處理的模式會形成服務器資源極大的浪費。以下圖所示,傳統 web 服務器每每把一個進程或線程做爲時間消費者,當一個請求產生的事件被該進程處理時,直到這個請求處理結束時,進程資源都將被這一請求所佔用。比較典型的例子如 Apache 同步阻塞的多進程模式就是這樣的。異步
傳統 web 服務器處理事件的簡單模型(矩形表明進程): 模塊化
Nginx 採用事件驅動架構處理業務的方式與傳統的 web 服務器是不一樣的。它不使用進程或者線程來做爲事件消費者,所謂的事件消費者只能是某個模塊。只有事件收集、分發器纔有資格佔用進程資源,它們會在分發某個事件時調用事件消費模塊使用當前佔用的進程資源,以下圖所示,該圖中列出了 5 個不一樣的事件,在事件收集、分發者進程的一次處理過程當中,這 5 個事件按照順序被收集後,將開始使用當前進程分發事件,從而調用相應的事件消費者來處理事件。固然,這種分發、調用也是有序的。性能
Nginx 處理事件的簡單模型:
由上圖能夠看出,處理請求事件時,Nginx 的事件消費者只是被事件分發者進程短時間調用而已,這種設計使得網絡性能、用戶感知的請求時延都獲得了提高,每一個用戶的請求所產生的事件會及時響應,整個服務器的網絡吞吐量都會因爲事件的及時響應而增大。固然,這也帶來必定的要求,即每一個事件消費者都不能有阻塞行爲,不然將會因爲長時間佔用事件分發者進程而致使其餘事件得不到及時響應,Nginx 的非阻塞特性就是因爲它的模塊都是知足這個要求的。
多階段異步處理請求與事件驅動架構是密切相關的,也就是說,請求的多階段異步處理只能基於事件驅動架構實現。多階段異步處理就是把一個請求的處理過程按照事件的觸發方式劃分爲多個階段,每一個階段均可以由事件收集、分發器來觸發。
處理獲取靜態文件的 HTTP 請求時切分的階段及各階段的觸發事件以下所示:
這個例子中,該請求大體分爲 7 個階段,這些階段是能夠重複發生的,所以,一個下載靜態資源請求可能會因爲請求數據過大,網速不穩定等因素而被分解爲成百上千個上圖所列出的階段。
異步處理和多階段是相輔相成的,只有把請求分爲多個階段,纔有所謂的異步處理。當一個時間被分發到事件消費者中進行處理時,事件消費者處理完這個事件只至關於處理完 1 個請求的階段。何時能夠處理下一個階段呢?這隻能等待內核的通知,即當下一次事件出現時,epoll 等事件分發器將會獲取到通知,而後去調用事件消費者進行處理。
Nginx 在啓動後,會有一個 master 進程和多個 worker 進程。master 進程主要用來管理worker 進程,包括接收來自外界的信號,向各 worker 進程發送信號,監控 worker 進程的運行狀態以及啓動 worker 進程。 worker 進程是用來處理來自客戶端的請求事件。多個 worker 進程之間是對等的,它們同等競爭來自客戶端的請求,各進程互相獨立,一個請求只能在一個 worker 進程中處理。worker 進程的個數是能夠設置的,通常會設置與機器 CPU 核數一致,這裏面的緣由與事件處理模型有關。Nginx 的進程模型,可由下圖來表示:
在服務器上查看 Nginx 進程:
這種設計帶來如下優勢:
1) 利用多核系統的併發處理能力
現代操做系統已經支持多核 CPU 架構,這使得多個進程能夠分別佔用不一樣的 CPU 核心來工做。Nginx 中全部的 worker 工做進程都是徹底平等的。這提升了網絡性能、下降了請求的時延。
2) 負載均衡
多個 worker 工做進程經過進程間通訊來實現負載均衡,即一個請求到來時更容易被分配到負載較輕的 worker 工做進程中處理。這也在必定程度上提升了網絡性能、下降了請求的時延。
3) 管理進程會負責監控工做進程的狀態,並負責管理其行爲
管理進程不會佔用多少系統資源,它只是用來啓動、中止、監控或使用其餘行爲來控制工做進程。首先,這提升了系統的可靠性,當 worker 進程出現問題時,管理進程能夠啓動新的工做進程來避免系統性能的降低。其次,管理進程支持 Nginx 服務運行中的程序升級、配置項修改等操做,這種設計使得動態可擴展性、動態定製性較容易實現。
爲了不出現內存碎片,減小向操做系統申請內存的次數、下降各個模塊的開發複雜度,Nginx 設計了簡單的內存池,它的做用主要是把屢次向系統申請內存的操做整合成一次,這大大減小了 CPU 資源的消耗,同時減小了內存碎片。
所以,一般每個請求都有一個簡易的獨立內存池(如每一個 TCP 鏈接都分配了一個內存池),而在請求結束時則會銷燬整個內存池,把曾經分配的內存一次性歸還給操做系統。這種設計大大提升了模塊開發的簡單些,由於在模塊申請內存後不用關心它的釋放問題;並且由於分配內存次數的減小使得請求執行的時延獲得了下降。同時,經過減小內存碎片,提升了內存的有效利用率和系統可處理的併發鏈接數,從而加強了網絡性能。