一文帶你搞懂Nginx如何處理請(qǐng)求
Nginx(Engine X)是一個(gè)高性能的HTTP和反向代理服務(wù)器,它以其高并發(fā)、高性能和低資源消耗著稱。這篇文章,我們將從原理、代碼以及示例來(lái)深入分析 Nginx如何處理請(qǐng)求。
Nginx請(qǐng)求處理原理
Nginx請(qǐng)求處理的原理主要涉及以下 6個(gè)核心技術(shù)點(diǎn):
- 事件驅(qū)動(dòng)模型
- 異步非阻塞處理
- 進(jìn)程模型
- 模塊化設(shè)計(jì)
- 負(fù)載均衡和反向代理
- 配置文件解析
接下來(lái)我們將一一分析它們:
事件驅(qū)動(dòng)模型
Nginx的事件驅(qū)動(dòng)模型基于非阻塞 I/O 和事件循環(huán)。它使用多路復(fù)用技術(shù)(如 epoll、kqueue 等)來(lái)監(jiān)控多個(gè)連接,并在事件發(fā)生時(shí)調(diào)用相應(yīng)的處理器。
多路復(fù)用技術(shù)
多路復(fù)用技術(shù)是事件驅(qū)動(dòng)模型的關(guān)鍵。Nginx 支持多種多路復(fù)用機(jī)制,包括:
- epoll:Linux 平臺(tái)上的高效多路復(fù)用機(jī)制,適用于大量并發(fā)連接。
- kqueue:FreeBSD、OpenBSD、macOS 等平臺(tái)的多路復(fù)用機(jī)制。
- select 和 poll:較早期的多路復(fù)用機(jī)制,適用于較少的并發(fā)連接。
Nginx 會(huì)根據(jù)操作系統(tǒng)的不同自動(dòng)選擇最優(yōu)的多路復(fù)用機(jī)制。
事件循環(huán)
在 Nginx 中,事件循環(huán)主要負(fù)責(zé)監(jiān)控和處理網(wǎng)絡(luò)事件。其基本流程如下:
- 初始化:初始化事件模塊,配置事件處理機(jī)制(如 epoll)。
- 事件監(jiān)聽(tīng):監(jiān)聽(tīng)客戶端連接請(qǐng)求、數(shù)據(jù)可讀可寫(xiě)等事件。
- 事件檢測(cè):通過(guò)多路復(fù)用機(jī)制檢測(cè)事件的發(fā)生。
- 事件分發(fā):將檢測(cè)到的事件分發(fā)給對(duì)應(yīng)的事件處理器。
- 事件處理:調(diào)用回調(diào)函數(shù)來(lái)處理具體的事件,如讀取請(qǐng)求、發(fā)送響應(yīng)等。
事件處理
Nginx 的事件處理是通過(guò)一系列的回調(diào)函數(shù)來(lái)實(shí)現(xiàn)的,這些回調(diào)函數(shù)在不同的事件階段被調(diào)用,包括:
- 連接建立:處理新連接的建立。
- 請(qǐng)求讀?。簭目蛻舳俗x取請(qǐng)求數(shù)據(jù)。
- 響應(yīng)發(fā)送:向客戶端發(fā)送響應(yīng)數(shù)據(jù)。
- 連接關(guān)閉:處理連接的關(guān)閉。
###Worker 進(jìn)程
Nginx 使用多進(jìn)程架構(gòu),其中每個(gè) Worker 進(jìn)程都是一個(gè)獨(dú)立的事件驅(qū)動(dòng)服務(wù)器。Master 進(jìn)程負(fù)責(zé)管理 Worker 進(jìn)程,而 Worker 進(jìn)程則負(fù)責(zé)處理客戶端請(qǐng)求。每個(gè) Worker 進(jìn)程都有自己的事件循環(huán),能夠獨(dú)立處理并發(fā)連接。
異步非阻塞處理
異步非阻塞意味著 Nginx在處理一個(gè)請(qǐng)求時(shí),可以進(jìn)行 I/O操作而不被阻塞,當(dāng)請(qǐng)求發(fā)起后,處理過(guò)程中的任何耗時(shí)操作(如磁盤(pán)I/O,網(wǎng)絡(luò)I/O)都不會(huì)阻塞整個(gè)處理。Nginx通過(guò)將這些操作放在異步事件中等待完成,釋放工作進(jìn)程來(lái)處理其他可用事件。
進(jìn)程模型
Nginx 采用了 Master-Worker 多進(jìn)程架構(gòu),其中包含一個(gè)主進(jìn)程(Master Process)和一個(gè)或多個(gè)工作進(jìn)程(Worker Processes)。這種架構(gòu)的設(shè)計(jì)可以確保責(zé)任分離,以便更好地管理系統(tǒng)資源、并發(fā)請(qǐng)求處理與故障恢復(fù)。
Master進(jìn)程
職責(zé)
- Master進(jìn)程的主要職責(zé)是管理和控制工作進(jìn)程。
- 接收和處理來(lái)自外部(如管理員)的請(qǐng)求,例如配置重載、啟動(dòng)或關(guān)閉 Nginx。
- 在啟動(dòng)時(shí)讀取和解析配置文件,初始化各種全局變量和資源。
主要功能
- 啟動(dòng)和終止 Worker 進(jìn)程:Master 進(jìn)程負(fù)責(zé)分批啟動(dòng)工作進(jìn)程,并根據(jù)需要重啟或終止工作進(jìn)程。
- 管理信號(hào):監(jiān)聽(tīng)和處理系統(tǒng)信號(hào)(如 SIGHUP 用于重載配置,SIGTERM 用于關(guān)閉服務(wù)器等)。
- 平滑升級(jí):做出配置更改或執(zhí)行 Nginx 版本升級(jí)時(shí),可以通過(guò)不間斷地重新啟動(dòng)工作進(jìn)程實(shí)現(xiàn)平滑升級(jí)。
Worker進(jìn)程
職責(zé)
- 處理客戶端請(qǐng)求的所有工作。所有實(shí)際的網(wǎng)絡(luò)事件處理都在工作進(jìn)程中進(jìn)行,包括接受連接、讀取請(qǐng)求、處理請(qǐng)求、發(fā)送響應(yīng),等等。
- 每個(gè) Worker 進(jìn)程都是相互獨(dú)立的,同時(shí)可以處理獨(dú)立的連接。
主要特性和功能
- 無(wú)共享狀態(tài):每個(gè) Worker 內(nèi)存相互獨(dú)立,這樣可以避免在編程中可能出現(xiàn)的很多鎖問(wèn)題,提高并行處理效率。
- 事件驅(qū)動(dòng)和非阻塞 I/O:Worker 進(jìn)程使用非阻塞 I/O 和事件驅(qū)動(dòng)機(jī)制(如 epoll、kqueue 等)來(lái)處理請(qǐng)求,這使得在同一時(shí)間可以處理大量的并發(fā)請(qǐng)求。
- 進(jìn)程模型的競(jìng)爭(zhēng)連接機(jī)制:所有 Worker 進(jìn)程均平等地受到請(qǐng)求連接,具體哪個(gè)進(jìn)程處理新連接由端口復(fù)用技術(shù)和操作系統(tǒng)決定。
進(jìn)程之間的通信
Nginx 的 Master 和 Worker 進(jìn)程之間使用 UNIX 信號(hào)進(jìn)行簡(jiǎn)單而有效的通信,Master 進(jìn)程對(duì)信號(hào)的響應(yīng)可以帶來(lái)整體行為的變化。例如:
SIGHUP:重新加載配置,此時(shí) Master進(jìn)程會(huì)完成以下幾件事:
- 檢查語(yǔ)法錯(cuò)誤后生成新配置。
- 啟動(dòng)新的 Worker 進(jìn)程。
- 逐漸關(guān)閉舊的 Worker 進(jìn)程,以便不會(huì)中斷現(xiàn)有連接。
SIGTERM/SIGQUIT:優(yōu)雅地關(guān)閉 Nginx 服務(wù)器。此時(shí),Master 進(jìn)程會(huì)通知 Worker 進(jìn)程在所有當(dāng)前請(qǐng)求完成后關(guān)閉。
SIGUSR1:重新打開(kāi)日志文件,通常用于日志切換。
模塊化設(shè)計(jì)
Nginx以模塊化的方式設(shè)計(jì),通過(guò)不同類(lèi)型的模塊(如HTTP模塊、事件模塊、Mail模塊等)來(lái)完成不同功能。每種模塊提供了處理請(qǐng)求的特定功能,組合在一起完成完整的HTTP服務(wù)。
負(fù)載均衡和反向代理
Nginx可以配置為反向代理,在處理請(qǐng)求時(shí)直接轉(zhuǎn)發(fā)到后端服務(wù)器。它可以實(shí)現(xiàn)負(fù)載均衡,根據(jù)設(shè)定的策略(如輪詢、最少連接)來(lái)分配請(qǐng)求。
配置文件解析
Nginx通過(guò)配置文件執(zhí)行請(qǐng)求的處理定義。配置文件指定服務(wù)器塊、位置塊和其他配置指令,用以指示Nginx如何響應(yīng)不同的HTTP請(qǐng)求。
代碼分析
Nginx的代碼是用C語(yǔ)言編寫(xiě)的,下面我們分析一些關(guān)鍵的代碼片段來(lái)了解其工作原理。
啟動(dòng)流程
Nginx的啟動(dòng)從main
函數(shù)開(kāi)始,在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; // 初始化日志、信號(hào)處理等 ngx_log_error(NGX_LOG_NOTICE, log, 0, "nginx version: " NGINX_VERSION); // 獲取命令行參數(shù) process_args(argc, argv, &init_cycle); // 初始化周期 cycle = ngx_init_cycle(&init_cycle); // 循環(huán)處理到來(lái)的請(qǐng)求 ngx_process_events_and_timers(cycle); return 0; }
ngx_init_cycle
是初始化Nginx周期的函數(shù),其中包括配置文件的加載和解析。
事件循環(huán)
核心的事件循環(huán)位于ngx_process_events_and_timers
函數(shù)中:
void ngx_process_events_and_timers(ngx_cycle_t *cycle) { ngx_msec_t timer, delta; ngx_uint_t i; for (;;) { // 獲取即將觸發(fā)的事件和時(shí)間 timer = ngx_event_find_timer(); if (timer == NGX_TIMER_INFINITE) { timer = (ngx_msec_t) NGX_TIMER_INFINITE_VALUE; } // 等待事件到來(lái) (void) ngx_process_events(cycle, timer, 0); // 調(diào)用定時(shí)器事件 ngx_event_expire_timers(); // 處理延遲文件事件 ngx_handle_delayed_events(cycle); } }
在這個(gè)循環(huán)中,Nginx持續(xù)地等待事件的發(fā)生,然后根據(jù)事件的類(lèi)型執(zhí)行相應(yīng)的操作。
請(qǐng)求處理
實(shí)際的HTTP請(qǐng)求處理則是在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); }
在這個(gè)函數(shù)里,Nginx逐一執(zhí)行請(qǐng)求生命周期中的各個(gè)階段,包括URI解析,權(quán)限檢查,讀取請(qǐng)求體,以及最終的內(nèi)容處理。
示例配置
為了更好地理解 Nginx如何處理請(qǐng)求,在這里,我們通過(guò)一個(gè)簡(jiǎn)單的靜態(tài)網(wǎng)頁(yè)服務(wù)器的配置例子來(lái)說(shuō)明:
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使用一個(gè)worker進(jìn)程。worker_connections 1024;
:每個(gè)worker進(jìn)程最多支持1024個(gè)并發(fā)連接。http {}
:開(kāi)始一個(gè)HTTP配置上下文。server {}
:定義一個(gè)虛擬服務(wù)器。listen 80;
:服務(wù)器監(jiān)聽(tīng)80端口。server_name localhost;
:指定服務(wù)器名。location / {}
:定義根目錄的請(qǐng)求處理位置。root /var/www/html;
:將所有對(duì)根目錄的請(qǐng)求映射到文件系統(tǒng)的/var/www/html
目錄。index index.html index.htm;
:指定默認(rèn)的首頁(yè)文件。
總結(jié)
Nginx的設(shè)計(jì)和實(shí)現(xiàn)牢牢把握住了高性能和高并發(fā)的目標(biāo),通過(guò)事件驅(qū)動(dòng)模型、異步處理、多進(jìn)程架構(gòu)以及豐富的模塊系統(tǒng),Nginx不僅可以高效地處理HTTP請(qǐng)求,還可以通過(guò)模塊化的配置系統(tǒng)進(jìn)行極為靈活的部署和定制。通過(guò)深入Nginx的代碼和運(yùn)行原理,我們可以更好地理解和優(yōu)化我們的Nginx服務(wù)器配置。
以上就是一文帶你搞懂Nginx如何處理請(qǐng)求的詳細(xì)內(nèi)容,更多關(guān)于Nginx處理請(qǐng)求的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
nginx配置proxy_pass后返回404問(wèn)題以及Nginx host相關(guān)變量的說(shuō)明
這篇文章主要介紹了nginx配置proxy_pass后返回404問(wèn)題以及Nginx host相關(guān)變量的說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01nginx結(jié)合openssl實(shí)現(xiàn)https的方法
這篇文章主要介紹了基于nginx結(jié)合openssl實(shí)現(xiàn)https的方法,準(zhǔn)備工作大家需要安裝nginx服務(wù),具體操作過(guò)程跟隨小編一起看看吧2021-07-07Nginx配置80端口訪問(wèn)8080及項(xiàng)目名地址方法解析
這篇文章主要介紹了Nginx配置80端口訪問(wèn)8080及項(xiàng)目名地址方法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09Nginx出現(xiàn)“Too many open files”問(wèn)題的解決方法
在進(jìn)行壓力測(cè)試時(shí),自建CDN節(jié)點(diǎn)的Nginx可能會(huì)出現(xiàn)“Too many open files”錯(cuò)誤,這通常意味著Nginx嘗試打開(kāi)的文件數(shù)量超出了系統(tǒng)的限制,本文將詳細(xì)介紹如何識(shí)別和解決這一問(wèn)題,確保Nginx在負(fù)載較高時(shí)仍能正常運(yùn)行,需要的朋友可以參考下2024-10-10Nginx反向代理之proxy_redirect指令的實(shí)現(xiàn)
proxy_redirect指令是用來(lái)重置頭信息中的"Location"和"Refresh"的值,本文就來(lái)詳細(xì)的介紹一下如何使用,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08關(guān)于Nginx中虛擬主機(jī)的一些冷門(mén)知識(shí)小結(jié)
這篇文章主要給大家介紹了關(guān)于Nginx中虛擬主機(jī)的一些冷門(mén)知識(shí),文中通過(guò)圖文以及實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-03-03