Nginx 如何處理請求?這篇文章幫你講透!
Nginx(Engine X)是一個高性能的HTTP和反向代理服務器,它以其高并發、高性能和低資源消耗著稱。這篇文章,我們將從原理、代碼以及示例來深入分析 Nginx如何處理請求。
一、Nginx請求處理原理
Nginx請求處理的原理主要涉及以下 6個核心技術點:
- 事件驅動模型
- 異步非阻塞處理
- 進程模型
- 模塊化設計
- 負載均衡和反向代理
- 配置文件解析
接下來我們將一一分析它們:
1. 事件驅動模型
Nginx的事件驅動模型基于非阻塞 I/O 和事件循環。它使用多路復用技術(如 epoll、kqueue 等)來監控多個連接,并在事件發生時調用相應的處理器。
(1) 多路復用技術
多路復用技術是事件驅動模型的關鍵。Nginx 支持多種多路復用機制,包括:
- epoll:Linux 平臺上的高效多路復用機制,適用于大量并發連接。
- kqueue:FreeBSD、OpenBSD、macOS 等平臺的多路復用機制。
- select 和 poll:較早期的多路復用機制,適用于較少的并發連接。
Nginx 會根據操作系統的不同自動選擇最優的多路復用機制。
(2) 事件循環
在 Nginx 中,事件循環主要負責監控和處理網絡事件。其基本流程如下:
- 初始化:初始化事件模塊,配置事件處理機制(如 epoll)。
- 事件監聽:監聽客戶端連接請求、數據可讀可寫等事件。
- 事件檢測:通過多路復用機制檢測事件的發生。
- 事件分發:將檢測到的事件分發給對應的事件處理器。
- 事件處理:調用回調函數來處理具體的事件,如讀取請求、發送響應等。
(3) 事件處理
Nginx 的事件處理是通過一系列的回調函數來實現的,這些回調函數在不同的事件階段被調用,包括:
- 連接建立:處理新連接的建立。
- 請求讀取:從客戶端讀取請求數據。
- 響應發送:向客戶端發送響應數據。
- 連接關閉:處理連接的關閉。
(4) Worker 進程
Nginx 使用多進程架構,其中每個 Worker 進程都是一個獨立的事件驅動服務器。Master 進程負責管理 Worker 進程,而 Worker 進程則負責處理客戶端請求。每個 Worker 進程都有自己的事件循環,能夠獨立處理并發連接。
2. 異步非阻塞處理
異步非阻塞意味著 Nginx在處理一個請求時,可以進行 I/O操作而不被阻塞,當請求發起后,處理過程中的任何耗時操作(如磁盤I/O,網絡I/O)都不會阻塞整個處理。Nginx通過將這些操作放在異步事件中等待完成,釋放工作進程來處理其他可用事件。
3. 進程模型
Nginx 采用了 Master-Worker 多進程架構,其中包含一個主進程(Master Process)和一個或多個工作進程(Worker Processes)。這種架構的設計可以確保責任分離,以便更好地管理系統資源、并發請求處理與故障恢復。
(1) Master進程
職責:
- Master進程的主要職責是管理和控制工作進程。
- 接收和處理來自外部(如管理員)的請求,例如配置重載、啟動或關閉 Nginx。
- 在啟動時讀取和解析配置文件,初始化各種全局變量和資源。
主要功能:
- 啟動和終止 Worker 進程:Master 進程負責分批啟動工作進程,并根據需要重啟或終止工作進程。
- 管理信號:監聽和處理系統信號(如 SIGHUP 用于重載配置,SIGTERM 用于關閉服務器等)。
- 平滑升級:做出配置更改或執行 Nginx 版本升級時,可以通過不間斷地重新啟動工作進程實現平滑升級。
(2) Worker進程
職責:
- 處理客戶端請求的所有工作。所有實際的網絡事件處理都在工作進程中進行,包括接受連接、讀取請求、處理請求、發送響應,等等。
- 每個 Worker 進程都是相互獨立的,同時可以處理獨立的連接。
主要特性和功能:
- 無共享狀態:每個 Worker 內存相互獨立,這樣可以避免在編程中可能出現的很多鎖問題,提高并行處理效率。
- 事件驅動和非阻塞 I/O:Worker 進程使用非阻塞 I/O 和事件驅動機制(如 epoll、kqueue 等)來處理請求,這使得在同一時間可以處理大量的并發請求。
- 進程模型的競爭連接機制:所有 Worker 進程均平等地收到請求連接,具體哪個進程處理新連接由端口復用技術和操作系統決定。
(3) 進程之間的通信
① Nginx 的 Master 和 Worker 進程之間使用 UNIX 信號進行簡單而有效的通信,Master 進程對信號的響應可以帶來整體行為的變化。例如:
SIGHUP:重新加載配置,此時 Master進程會完成以下幾件事:
- 檢查語法錯誤后生成新配置。
- 啟動新的 Worker 進程。
- 逐漸關閉舊的 Worker 進程,以便不會中斷現有連接。
② SIGTERM/SIGQUIT:優雅地關閉 Nginx 服務器。此時,Master 進程會通知 Worker 進程在所有當前請求完成后關閉。
③ SIGUSR1:重新打開日志文件,通常用于日志切換。
4. 模塊化設計
Nginx以模塊化的方式設計,通過不同類型的模塊(如HTTP模塊、事件模塊、Mail模塊等)來完成不同功能。每種模塊提供了處理請求的特定功能,組合在一起完成完整的HTTP服務。
5. 負載均衡和反向代理
Nginx可以配置為反向代理,在處理請求時直接轉發到后端服務器。它可以實現負載均衡,根據設定的策略(如輪詢、最少連接)來分配請求。
6. 配置文件解析
Nginx通過配置文件執行請求的處理定義。配置文件指定服務器塊、位置塊和其他配置指令,用以指示Nginx如何響應不同的HTTP請求。
二、代碼分析
Nginx的代碼是用C語言編寫的,下面我們分析一些關鍵的代碼片段來了解其工作原理。
1. 啟動流程
Nginx的啟動從main函數開始,在src/core/nginx.c文件中:
int main(int argc, char *const *argv) {
ngx_log_t *log;
ngx_cycle_t *cycle, init_cycle;
ngx_core_conf_t *ccf;
ngx_conf_t cf;
// 初始化日志、信號處理等
ngx_log_error(NGX_LOG_NOTICE, log, 0, "nginx version: " NGINX_VERSION);
// 獲取命令行參數
process_args(argc, argv, &init_cycle);
// 初始化周期
cycle = ngx_init_cycle(&init_cycle);
// 循環處理到來的請求
ngx_process_events_and_timers(cycle);
return 0;
}
ngx_init_cycle是初始化Nginx周期的函數,其中包括配置文件的加載和解析。
2. 事件循環
核心的事件循環位于ngx_process_events_and_timers函數中:
void ngx_process_events_and_timers(ngx_cycle_t *cycle) {
ngx_msec_t timer, delta;
ngx_uint_t i;
for (;;) {
// 獲取即將觸發的事件和時間
timer = ngx_event_find_timer();
if (timer == NGX_TIMER_INFINITE) {
timer = (ngx_msec_t) NGX_TIMER_INFINITE_VALUE;
}
// 等待事件到來
(void) ngx_process_events(cycle, timer, 0);
// 調用定時器事件
ngx_event_expire_timers();
// 處理延遲文件事件
ngx_handle_delayed_events(cycle);
}
}
在這個循環中,Nginx持續地等待事件的發生,然后根據事件的類型執行相應的操作。
3. 請求處理
實際的HTTP請求處理則是在ngx_http_process_request中完成:
void ngx_http_process_request(ngx_http_request_t *r) {
ngx_connection_t *c;
ngx_http_core_main_conf_t *cmcf;
c = r->connection;
// 處理的階段分為不同的handler
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
// Match the location
ngx_http_core_find_config_phase(r);
// Perform access checks
if (ngx_http_process_request_uri(r) != NGX_OK) {
return;
}
// Execute input body filters
if (ngx_http_read_client_request_body(r, ngx_http_request_body_handler) >= NGX_HTTP_SPECIAL_RESPONSE) {
return;
}
// Call content handler
ngx_http_core_content_phase(r);
}
在這個函數里,Nginx逐一執行請求生命周期中的各個階段,包括URI解析,權限檢查,讀取請求體,以及最終的內容處理。
三、示例配置
為了更好地理解 Nginx如何處理請求,在這里,我們通過一個簡單的靜態網頁服務器的配置例子來說明:
worker_processes 1;
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name localhost;
location / {
root /var/www/html;
index index.html index.htm;
}
}
}
配置分析:
- worker_processes 1;:指定Nginx使用一個worker進程。
- worker_connections 1024;:每個worker進程最多支持1024個并發連接。
- http {}:開始一個HTTP配置上下文。
- server {}:定義一個虛擬服務器。
- listen 80;:服務器監聽80端口。
- server_name localhost;:指定服務器名。
- location / {}:定義根目錄的請求處理位置。
- root /var/www/html;:將所有對根目錄的請求映射到文件系統的/var/www/html目錄。
- index index.html index.htm;:指定默認的首頁文件。
四、總結
Nginx是一款優秀的反向代理框架,它的設計和實現牢牢把握住了高性能和高并發的目標,通過事件驅動模型、異步處理、多進程架構以及豐富的模塊系統,Nginx不僅可以高效地處理HTTP請求,還可以通過模塊化的配置系統進行極為靈活的部署和定制。通過深入Nginx的代碼和運行原理,我們可以更好地理解和優化我們的Nginx服務器配置。