我們都知道nginx是以其高效能、穩定性、豐富的功能、簡單的配置和低資源消耗而出名的,那麼為什麼nginx能如此之快呢?下面我們就來從底層原理來分析。
Nginx 的進程模型
Nginx 伺服器,在正常運作過程中:
多進程:一個Master 進程、多個Worker 進程。
Master 流程:管理 Worker 流程。
對外介面:接收外部的操作(訊號);
對內轉送:根據外部的操作的不同,透過訊號管理Worker;
監控:監控Worker 進程的運作狀態,Worker 進程異常終止後,自動重新啟動Worker 進程。
Worker 進程:所有 Worker 進程都是平等的。
實際處理:網路請求,由 Worker 程序處理。
Worker 進程數:在 nginx.conf 中配置,一般設定為核心數,充分利用 CPU 資源,同時,避免進程數量過多,避免進程競爭 CPU 資源,增加上下文切換的損耗。
思考:
請求是連接到 Nginx,Master 流程負責處理和轉送?
如何選定哪個 Worker 行程處理請求?
請求的處理結果,是否還要經過 Master 流程?
HTTP 連線建立和請求處理程序如下:
#Nginx 啟動時,Master 進程,載入設定檔。 Master 進程,初始化監聽的 Socket。 Master 進程,Fork 出多個 Worker 流程。 Worker 流程,競爭新的連接,獲勝方透過三次握手,建立 Socket 連接,並處理請求。
Nginx 高效能、高並發
Nginx 為什麼擁有高效能並且能夠支撐高並發?
Nginx 採用多進程 非同步非阻塞方式(IO 多路復用 Epoll)。請求的完整過程:建立連結→讀取請求→解析請求→處理請求→回應請求。請求的完整過程對應到底層就是:讀寫 Socket 事件。
Nginx 的事件處理模型
Request:Nginx 中 HTTP 請求。
基本的 HTTP Web Server 工作模式:
接收請求:逐行讀取請求行和請求頭,判斷段有請求體後,讀取請求體。處理請求。回傳回應:根據處理結果,產生對應的 HTTP 請求(回應行、回應頭、回應體)。
Nginx 也是這個套路,整體流程一致:
#模組化體系架構
##Nginx的模組根據其功能基本上可以分為以下幾種類型:①event module:搭建了獨立於作業系統的事件處理機制的框架,及提供了各具體事件的處理。包括 ngx_events_module,ngx_event_core_module 和 ngx_epoll_module 等。 Nginx 具體使用何種事件處理模組,這依賴於具體的作業系統和編譯選項。 ②phase handler:此類型的模組也直接稱為 handler 模組。主要負責處理客戶端請求並產生待回應內容,例如 ngx_http_static_module 模組,負責客戶端的靜態頁面請求處理並將對應的磁碟檔案準備為回應內容輸出。 ③output filter:也稱為 filter 模組,主要是負責對輸出的內容進行處理,可以修改輸出。 例如,可以實現對輸出的所有 html 頁面增加預先定義的 footbar 一類的工作,或對輸出的圖片的 URL 進行替換之類的工作。 ④upstream:upstream 模組實現反向代理的功能,將真正的請求轉發到後端伺服器上,並從後端伺服器上讀取回應,發回客戶端。 upstream 模組是一種特殊的 handler,只不過回應內容不是真正由自己產生的,而是從後端伺服器上讀取的。 ⑤load-balancer:負載平衡模組,實作特定的演算法,在眾多的後端伺服器中,選擇一個伺服器出來作為某個請求的轉送伺服器。 常見問題剖析:Nginx vs ApacheNginx:IO 多路復用,Epoll(freebsd 上是kqueue)高效能高並發佔用系統資源少Apache:阻塞多進程/多執行緒更穩定,Bug 少模組更豐富Nginx 最大連線數基礎背景:Nginx 是多進程模型,Worker 進程用於處理請求。單一進程的連接數(檔案描述符 fd),有上限(nofile):ulimit -n。 Nginx 上配置單一 Worker 程序的最大連線數:worker_connections 上限為 nofile。 Nginx 上配置 Worker 進程的數量:worker_processes。
因此,Nginx 的最大連線數:
Nginx 的最大連線數:Worker 進程數 x 單一 Worker 進程的最大連線數。上面是 Nginx 作為通用伺服器時,最大的連線數。 Nginx 作為反向代理伺服器時,能夠服務的最大連線數:(Worker 進程數量 x 單一 Worker 進程的最大連線數)/ 2。 Nginx 反向代理時,會建立 Client 的連線和後端 Web Server 的連接,佔用 2 個連線。
思考:
每開啟一個 Socket 佔用一個 fd?為什麼,一個行程能夠開啟的 fd 數量有限制?
HTTP 請求和回應
HTTP 請求:
請求行:method、uri、http version請求頭請求體
HTTP 回應:
回應行:http version、status code回應頭響應體
IO 模型
處理多個請求時,可以採用:IO 多路復用或阻塞IO 多執行緒:
IO 多路復用:一個線程,追蹤多個Socket 狀態,哪個就緒,就讀寫哪個。阻塞 IO 多執行緒:每一個請求,新建一個服務執行緒。
IO 多路復用和多執行緒的適用場景?
IO 多重化:單一連線的請求處理速度沒有優勢。大並發量:只使用一個線程,處理大量的並發請求,降低上下文環境切換損耗,也不需要考慮並發問題,相對可以處理更多的請求。消耗更少的系統資源(不需要執行緒調度開銷)。適用於長連線的情況(多執行緒模式長連線容易造成執行緒過多,造成頻繁調度)。阻塞 IO 多執行緒:實作簡單,可以不依賴系統呼叫。每個線程,都需要時間和空間。當線程數量增長時,線程調度開銷指數增長。
select/poll 和epoll 比較如下:
select/poll 系統呼叫:
// select 系统调用 int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout); // poll 系统调用 int poll(struct pollfd fds[], nfds_t nfds, int timeout);
select:
查詢fd_set 中,是否有就緒的fd ,可以設定一個超時時間,當有fd (File descripter) 就緒或逾時返回。 fd_set 是一個位元集合,大小是在編譯內核時的常數,預設大小為 1024。特點:連線數限制,fd_set 可表示的 fd 數量太小了;線性掃描:判斷 fd 是否就緒,需要遍歷一邊 fd_set;資料複製:使用者空間和核心空間,複製連線就緒狀態資訊。
poll:
解決了連線數限制:poll 中將 select 中的 fd_set 替換成了一個 pollfd 數組,解決 fd 數量過小的問題。資料複製:使用者空間和核心空間,複製連線就緒狀態資訊。
epoll,event 事件驅動:
事件機制:避免線性掃描,為每個 fd,註冊一個監聽事件,fd 變更為就緒時,將 fd 新增至就緒鍊錶。 fd 數量:無限制(OS 等級的限制,單一行程能開啟多少個 fd)。
select,poll,epoll:
I/O 多工的機制。 I/O 多路復用就通過一種機制,可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或寫就緒),能夠通知程式進行對應的讀寫操作;監視多個文件描述符。但select,poll,epoll 本質上都是同步I/O:用戶進程負責讀寫(從核心空間拷貝到用戶空間),讀寫過程中,用戶進程是阻塞的;非同步IO,無需用戶進程負責讀寫,非同步IO,會負責從核心空間拷貝到使用者空間。
Nginx 的同時處理能力
關於 Nginx 的同時處理能力:同時連接數,一般最佳化後,峰值能保持在 1~3w 左右。 (記憶體和 CPU 核心數不同,會有進一步最佳化空間)
相關推薦:nginx教學
以上是nginx為什麼這麼快的詳細內容。更多資訊請關注PHP中文網其他相關文章!