面試官:說說看Nginx是如何處理請求的?為什么Nginx不采用多線程模型?Nginx負載均衡的算法有哪些?什么是正向和反向代理

面試官:說說看Nginx是如何處理請求的?
當客戶端發起一個請求時,Nginx的工作進程會監聽網絡端口,接收客戶端的連接請求。以下是Nginx處理請求的具體流程:
(1) 接收連接請求:Nginx接收到客戶端的連接請求后,會為該連接分配一個連接對象(ngx_connection_t)。連接對象包含連接的狀態信息、讀寫事件處理器等。
(2) 讀取請求頭信息:Nginx從客戶端讀取請求頭信息。請求頭包含HTTP方法(如GET、POST)、URL、HTTP版本以及各種請求頭字段(如Host、User-Agent、Content-Length等)。
(3) 解析請求頭信息:Nginx解析請求頭信息,提取必要的參數,如請求方法、URI、Host等。解析后的請求頭信息存儲在ngx_http_request_t結構體中。
(4) 查找匹配的虛擬主機和location塊:
- Nginx根據請求頭中的Host字段查找匹配的虛擬主機(server塊)。每個虛擬主機可以配置不同的域名和監聽端口。
- 在找到匹配的虛擬主機后,Nginx繼續查找與請求URI匹配的location塊。location塊定義了如何處理特定路徑的請求。
(5) 執行處理階段:Nginx的請求處理分為多個階段,每個階段可以由多個模塊處理。這些階段包括:
- rewrite phase:執行重寫規則,如URL重寫。
- post rewrite phase:處理重寫后的請求。
- preaccess phase:執行訪問控制前的檢查,如IP地址過濾。
- access phase:執行訪問控制,如身份驗證。
- postaccess phase:訪問權限控制后的處理。
- try-files:嘗試訪問文件或目錄。
- content phase:生成響應內容,如靜態文件服務、反向代理、FastCGI等。在這個階段,Nginx根據配置生成響應內容,這可能涉及讀取靜態文件、調用后端服務(如反向代理、FastCGI、uWSGI等)、生成動態內容等。
(6) 生成并發送響應:
- Nginx將生成的響應頭發送回客戶端。響應頭包含HTTP狀態碼、響應頭字段(如Content-Type、Content-Length等)。
- Nginx將生成的響應體發送回客戶端。響應體可以是靜態文件內容、后端服務返回的數據等。
(7) 關閉連接:一旦響應發送完畢,Nginx會關閉連接。如果啟用了keep-alive連接,則連接可以保持打開狀態,用于后續請求。
面試官:說說看Nginx的進程架構是怎樣的?為什么Nginx不使用多線程模型?

1. 進程模型
Nginx采用Master-Worker多進程架構,這種架構的設計可以確保責任分離,以便更好地管理系統資源、并發請求處理與故障恢復。
(1) Master-Worker架構:
① 主進程(Master Process):
- Nginx的核心組件,負責初始化Nginx、加載配置文件、創建Worker進程等。
- 監聽配置文件的變更,并在不重啟的情況下重新加載配置。
- 管理Worker進程的生命周期,包括啟動、停止和管理Worker進程。
- 不直接處理客戶端的請求,而是用于控制和管理Worker進程。
② 工作進程(Worker Process)
- Nginx的工作進程,負責處理客戶端的請求。
- 每個Worker進程都是一個完整的Nginx服務器,多個Worker進程之間是對等的。
- 每個Worker進程可以處理成千上萬的并發連接,Nginx的事件模型可以根據系統負載自動選擇合適的事件通知機制(如epoll)。
(2) 進程間協作:
- Master進程和Worker進程之間通過信號和共享內存進行通信。Master進程會向Worker進程發送信號以管理它們的生命周期(如啟動、停止、重啟等)。
- Worker進程之間通過共享內存和進程間通信(IPC)機制進行必要的數據共享和同步。
(3) 負載均衡:
Nginx通過多進程模型實現了負載均衡。當有新的客戶端連接請求到達時,這些連接會被平均分配給各個Worker進程,從而實現負載均衡。這種設計確保了Nginx能夠高效地處理大量并發連接,避免了單個進程成為瓶頸。
(4) 高可用性:
Nginx的多進程模型還提供了高可用性。當一個Worker進程出現故障時,Master進程會自動重新啟動一個新的Worker進程來替代原來的進程,從而保證服務器的高可用性。這種設計使得Nginx能夠在高負載和復雜環境下穩定運行。
2. 為什么Nginx不使用多線程模型?
Nginx選擇不使用多線程模型,而是采用多進程加異步非阻塞I/O的事件驅動模型,主要基于以下幾個原因:
(1) 資源隔離與穩定性
- 多進程模型下,每個工作進程都是獨立的,它們之間不會共享內存空間(除了通過共享內存等特定機制進行通信)。這種隔離性使得一個工作進程的崩潰不會影響到其他進程,從而提高了整個系統的穩定性。
- 在多線程模型中,線程之間共享進程的內存空間,這可能導致線程間的數據競爭、死鎖等問題,增加了系統的復雜性和調試難度。
(2) 利用多核CPU
- Nginx的多進程模型可以很好地利用現代操作系統提供的進程調度機制,將工作進程分配到不同的CPU核心上運行,從而實現并行處理。
- 雖然多線程模型也可以利用多核CPU,但線程的創建、切換和同步開銷通常比進程更高,尤其是在高并發場景下。
(3) 避免線程競爭和死鎖
- 在多線程模型中,多個線程可能同時訪問共享資源(如內存、文件等),這需要使用鎖機制來確保數據的一致性和安全性。然而,鎖的使用往往會導致線程競爭和死鎖問題,降低系統的性能。
- Nginx通過采用異步非阻塞I/O和事件驅動模型,避免了鎖的使用,從而減少了線程競爭和死鎖的風險。
(4) 簡化編程模型
- Nginx的多進程加異步非阻塞I/O模型相對簡單明了,開發者可以更容易地理解和維護代碼。
- 多線程編程往往涉及復雜的線程同步和通信機制,增加了編程的復雜性和出錯的可能性。
(5) 設計初衷
- Nginx的設計初衷就是為了提供一個高性能、低資源消耗的Web服務器和反向代理服務器。在設計之初,Nginx的開發者就選擇了多進程加異步非阻塞I/O的模型,并一直沿用至今。
- Nginx的社區和開發者群體也傾向于保持這種設計哲學,以確保Nginx的穩定性和性能優勢。
面試官:什么是正向代理和反向代理?Nginx如何實現正向代理和反向代理功能?
反向代理功能是指代理服務器接受互聯網上的連接請求,然后將這些請求轉發給內部網絡上的服務器,并將從內部服務器上得到的響應返回給互聯網上請求連接的客戶端。在這個過程中,代理服務器在外部世界中顯示為服務器。
一般來說反向代理中代理服務器和后臺服務是一伙兒的,綁定在一起??蛻舳瞬恢雷约簩嶋H請求的到底是誰。

現實中的反向代理例子有:負載均衡服務器、網絡安全防護(防DDoS攻擊) 和內容分發網絡 CDN等。
Nginx實現反向代理功能主要通過配置Nginx服務器,使其成為客戶端和目標服務器之間的中介。以下是Nginx實現反向代理功能的具體步驟和要點:
(1) 配置Nginx:
Nginx的反向代理配置主要在nginx.conf文件中進行,或者在包含的子配置文件中進行。
- 監聽端口:設置Nginx監聽的端口,默認為80端口,用于接收HTTP請求。
- 服務器名稱:定義Nginx服務器響應的域名。
- location塊:根據請求的URI進行匹配,并定義相應的操作,如反向代理。
- 反向代理指令(proxy_pass):指定請求應被轉發到的后端服務器的URL。
(2) 配置示例:
一個基本的反向代理配置示例如下:
http {
server {
listen 80; # 監聽80端口
server_name example.com; # 服務器名稱
location / {
proxy_pass http://backend-server:8080; # 后端服務器地址與端口
proxy_set_header Host $host; # 保留原始Host頭
proxy_set_header X-Real-IP $remote_addr; # 傳遞真實客戶端IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; # 傳遞請求協議(http/https)
# 其他可選配置,如緩存、超時、重試等
}
}
}正向代理用于將客戶端的請求轉發到目標服務器,并將服務器的響應返回給客戶端。在這種方式下,客戶端將請求發送給代理服務器,由代理服務器代替客戶端向目標服務器發出請求,并將目標服務器的響應返回給客戶端。通過正向代理,客戶端可以直接訪問外部網絡,而無需直接與目標服務器建立連接。

現實中的正向代理例子有:VPN。
Nginx作為高性能的Web服務器和反向代理服務器,也可以實現正向代理功能。以下是Nginx實現正向代理的具體步驟和配置方法:
Nginx正向代理的配置方法
由于默認的Nginx發布版本不支持正向代理功能,需要借助ngx_http_proxy_connect_module這個第三方插件來完成。在配置文件中添加正向代理的配置,例如:
server {
listen 3128; # 監聽端口
resolver 114.114.114.114; # DNS解析器
proxy_connect; # 允許CONNECT請求
proxy_connect_allow 443 563; # 允許連接的端口
proxy_connect_connect_timeout 10s; # 連接超時時間
proxy_connect_data_timeout 10s; # 數據傳輸超時時間
location / {
proxy_pass http://$host; # 轉發請求到目標服務器
proxy_set_header Host $host; # 設置請求頭
}
}以上配置中,listen指令指定了Nginx監聽的端口,resolver指令指定了DNS解析器的地址,proxy_connect等指令用于配置CONNECT請求的處理,location指令和proxy_pass指令則用于指定將請求轉發到哪個目標服務器。
面試官:什么是動態資源、靜態資源分離?為什么要做動、靜分離?Nginx怎么做的動靜分離?
動態資源與靜態資源分離(簡稱動、靜分離)是一種常見的Web應用架構模式。
1. 動態資源與靜態資源
(1) 靜態資源
當用戶多次訪問某個資源時,如果資源的源代碼不會發生改變,那么該資源就被稱為靜態資源。常見的靜態資源包括圖片(img)、樣式表(css)、腳本文件(js)、視頻(mp4)等。這些資源通??梢员粸g覽器和CDN(內容分發網絡)緩存,以減少對服務器的重復請求。
(2) 動態資源當
用戶多次訪問某個資源時,如果資源的源代碼可能會發生變化,那么該資源就被稱為動態資源。常見的動態資源包括JSP、FTL等服務器端腳本或模板文件。這些資源通常需要根據用戶的請求動態生成響應內容。
2. 動、靜分離的原因
(1) 性能優化
動態內容和靜態內容在處理和分發上存在差異。將它們分離可以分別進行優化,從而提高整體性能。例如,靜態資源可以由專門的靜態資源服務器(如Nginx)直接處理和提供,而動態資源則由應用服務器處理。這樣可以顯著減少對動態資源服務器的請求量,降低其負載。
(2) 緩存管理
靜態資源易于被緩存,而動態資源通常不適宜緩存或需要更精細的緩存控制。通過動、靜分離,可以更好地管理緩存策略,提高緩存命中率,減少服務器響應時間和帶寬消耗。
(3) 負載均衡
分離后,可以根據內容類型對資源進行優化分配,實現更有效的負載均衡。例如,可以根據動態資源的訪問量和特點,針對性地增加動態資源服務器的數量和規模,以應對高并發的訪問需求。
(4) 安全性增強
靜態內容服務器通常不需要執行復雜的程序代碼,因此攻擊面較小,可以降低安全風險。將其與執行動態代碼的服務器分離可以降低潛在的安全威脅。
以下是Nginx動靜分離的具體實現方式:
3. 配置方法
在Nginx中,可以通過location指令來實現動靜分離。location指令用于匹配請求的URI,并根據不同的路徑將請求分發給不同的處理模塊。
(1) 靜態資源處理
在上述配置中,location /static/和location /images/分別匹配以/static/和/images/開頭的請求,Nginx將在指定的root目錄下查找對應的文件。如果文件存在,Nginx會直接將文件返回給客戶端。location ~* \.(jpg|jpeg|png|gif|ico|css|js)$使用正則表達式匹配所有以這些擴展名結尾的請求,并設置緩存過期時間和Cache-Control頭部。
靜態資源通常直接由Nginx處理,因此可以在location塊中指定靜態資源的目錄。
使用正則表達式匹配靜態資源的文件擴展名,如.jpg、.jpeg、.png、.gif、.ico、.css、.js等。
配置示例:
server {
listen 80;
server_name www.example.com;
# 靜態資源處理
location /static/ {
root /var/www/example;
}
location /images/ {
root /var/www/example;
}
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
root /var/www/example;
expires 30d; # 設置靜態資源的緩存過期時間
add_header Cache-Control "public"; # 添加Cache-Control頭部,強化瀏覽器緩存行為
}
}(2) 動態請求處理
對于動態請求,可以將請求轉發給后端應用服務器(如PHP-FPM、Django、Node.js等)處理。
配置示例:
server {
listen 80;
server_name www.example.com;
# 動態請求處理
location ~ \.php$ {
root /var/www/example;
fastcgi_pass 127.0.0.1:9000; # 指定將這些請求轉發給PHP-FPM服務器處理
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}在上述配置中,location ~ \.php$使用正則表達式匹配所有以.php結尾的請求,并將這些請求視為動態請求。fastcgi_pass指定將這些請求轉發給PHP-FPM服務器處理,127.0.0.1:9000是PHP-FPM的監聽地址。其他參數用于設置FastCGI參數和請求的文件路徑。
4. 注意事項
(1) Nginx的location匹配優先級
Nginx的location匹配遵循特定的優先級規則,包括精確匹配(使用=符號)、正則表達式匹配(使用~或~*)、前綴匹配(普通location)。在配置多個location塊時,需要謹慎考慮它們之間的優先級關系,以確保正確的請求路由。
(2) 緩存策略
通過合理配置緩存,可以顯著提高網站性能。Nginx提供了強大而靈活的緩存功能,可以通過proxy_cache_path和proxy_cache指令來實現。對于靜態資源,可以設置較長的緩存過期時間,以減少對服務器的重復請求。
(3) 安全性
通過動靜分離,可以將靜態內容服務器與執行動態代碼的服務器分離,從而降低安全風險。靜態內容服務器通常不需要執行復雜的程序代碼,因此攻擊面較小。
面試官:Nginx負載均衡的算法策略有哪些?
1. 輪詢(Round Robin)
原理:輪詢算法按照服務器列表的順序依次分發請求。當一個新的請求到達時,Nginx會將其分配給列表中的下一個服務器,如果到達列表末尾,則重新開始循環。
特點:
- 簡單易用,無需額外配置。
- 適用于后端服務器性能相近的情況,因為每個服務器都會輪流接收到請求。
http {
upstream backend {
server backend1.example.com;
server backend2.example.com;
server backend3.example.com;
}
server {
location / {
proxy_pass http://backend;
}
}
}2. 加權輪詢(Weighted Round Robin)
原理:在輪詢的基礎上,為每個服務器分配一個權重值。權重值越高的服務器,接收到的請求越多。Nginx會根據權重值來計算每個服務器接收請求的比例。
特點:
- 考慮了服務器性能的差異,可以靈活分配請求。
- 需要手動配置權重值,以反映服務器的實際性能。
- 適用于后端服務器性能不均衡的情況,可以更好地利用服務器資源。
http {
upstream backend {
server backend1.example.com weight=3;
server backend2.example.com weight=2;
server backend3.example.com weight=1;
}
server {
location / {
proxy_pass http://backend;
}
}
}3. IP哈希(IP Hash)
原理:根據客戶端IP地址的哈希值來分配請求。Nginx會計算每個客戶端IP地址的哈希值,并使用該哈希值來選擇后端服務器。由于相同IP地址的哈希值相同,因此來自同一IP地址的請求總是被分配到同一臺后端服務器。
特點:
- 實現了會話粘性(Session Persistence),即同一個客戶端的請求總是被分配到同一臺后端服務器。
- 適用于需要保持會話一致性的場景,如購物車、用戶會話等。
- 但可能導致負載分布不均衡,因為某些IP地址范圍內的客戶端可能會頻繁訪問同一臺服務器。
http {
upstream backend {
ip_hash;
server backend1.example.com;
server backend2.example.com;
server backend3.example.com;
}
server {
location / {
proxy_pass http://backend;
}
}
}4. 最少連接(Least Connections)
原理:將請求分發到當前連接數最少的服務器上。Nginx會監控每臺后端服務器的當前連接數,并將新請求分配給連接數最少的服務器。
特點:
- 考慮了服務器的當前負載情況,可以更有效地平衡負載。
- 適用于長連接場景,如WebSocket、數據庫連接等。
- 但需要Nginx維護連接狀態,可能會增加一些開銷。
- Nginx本身不直接支持此策略,通常需要借助第三方模塊或自定義腳本實現。
http {
upstream backend {
least_conn;
server backend1.example.com;
server backend2.example.com;
server backend3.example.com;
}
server {
location / {
proxy_pass http://backend;
}
}
}5. Fair(第三方)
原理:根據后端服務器的響應時間來分配請求。Nginx會監控每臺后端服務器的響應時間,并將新請求分配給響應時間最短的服務器。
特點:
- 實現了更智能的負載均衡,可以根據服務器的實際性能來分配請求。
- 適用于對響應時間要求較高的場景。
- 但需要安裝第三方模塊(如nginx-module-vts)來實現。
6. URL哈希(URL Hash,第三方)
原理:根據請求URL的哈希值來分配請求。Nginx會計算每個請求URL的哈希值,并使用該哈希值來選擇后端服務器。由于相同URL的哈希值相同,因此相同URL的請求總是被分配到同一臺后端服務器。
特點:
- 提高了緩存的命中率,因為相同URL的請求總是被分配到同一臺后端服務器。
- 適用于緩存服務器集群。
- 但同樣可能導致負載分布不均衡。
- Nginx本身不支持此策略,需要安裝Nginx的hash軟件包來實現。




























