一、Nginx 整體架構概覽
1. Nginx簡介
Nginx 是采用 C 語言 編寫的高性能 Web 服務器、反向代理服務器及郵件代理服務器,特點是:高并發、高可用、低內存占用、模塊化設計。
架構核心理念:
-
Master-Worker 多進程模型
-
事件驅動(Event-Driven) + 異步非阻塞
-
高度模塊化設計
2. 進程模型
Nginx 的進程模型非常輕量,通常包含:
1. Master 進程
-
啟動時由 shell 進程 fork 出來
-
主要負責:
-
讀取配置文件
-
管理 Worker 子進程(啟動/重啟/關閉)
-
平滑升級(不間斷服務)
-
2. Worker 進程
-
實際處理請求的進程
-
默認情況下每個 CPU 一個 worker(可配置)
-
每個 worker 獨立工作,通過 epoll 等機制異步處理連接
3. Cache Manager/Loader(可選)
-
用于管理磁盤緩存(如 proxy_cache)
-
管理緩存的過期與空間
Worker 之間互不通信,避免鎖競爭,共享資源通常通過共享內存實現。
3. 事件驅動與異步非阻塞機制
Nginx 使用Reactor 模型 + 非阻塞 IO,事件模型封裝成 ngx_event
模塊,底層可選擇:
-
Linux:
epoll
-
BSD:
kqueue
-
Windows:
IOCP
-
Others:
select/poll
每個 worker 維護一個事件循環(event loop),處理以下類型事件:
-
新連接事件(accept)
-
讀寫事件(read/write)
-
定時器事件(超時)
-
信號事件
示例事件循環偽代碼:
while (true) {events = epoll_wait(epoll_fd, timeout);for (event in events) {if (event == read) {handle_read();} else if (event == write) {handle_write();}}process_timers();
}
4. 模塊化設計
Nginx 模塊系統非常強大,主程序僅提供框架,幾乎所有功能都通過模塊擴展。
模塊分類
類型 | 說明 |
---|---|
核心模塊 | 與配置、事件循環、進程管理相關 |
標準模塊 | 官方提供,如 http , gzip , rewrite |
第三方模塊 | 社區開發,如 lua-nginx-module , ngx_brotli |
事件模塊 | 封裝不同操作系統的 IO 模型(epoll/kqueue 等) |
過濾模塊 | 對請求或響應進行過濾、轉換(如 gzip, chunk) |
處理模塊 | 提供服務邏輯,如 ngx_http_proxy_module |
模塊生命周期 Hook:
模塊通過 Hook 函數注冊以下生命周期點:
-
postconfiguration
-
init_module
-
init_process
-
init_thread
-
exit_thread
-
exit_process
-
exit_master
5. 請求處理流程
以 HTTP 請求為例,完整流程如下:
1. 請求接收
-
Worker 進程通過 epoll 監聽 socket fd 上的 accept 事件
-
建立連接后封裝為
ngx_connection_t
2. 請求解析
-
使用狀態機解析 HTTP 請求頭、方法、URI 等
-
填充
ngx_http_request_t
結構
3. 查找 Location 配置
-
基于 URI 匹配 location block
-
加載對應 handler(如 proxy_pass)
4. 模塊鏈處理
Nginx 通過模塊鏈的方式處理請求,每個模塊處理一部分邏輯:
HTTP 請求
→ handler 模塊
→ access 模塊
→ rewrite 模塊
→ content 模塊(如 proxy)
→ filter 模塊(gzip,chunked)
→ output
每個模塊通過 ngx_http_module_t
結構中的回調函數插入處理鏈。
5. 響應輸出
-
各模塊處理完響應后輸出到客戶端
-
使用
sendfile
+writev
實現 零拷貝
6. 內存管理與連接池
Nginx 有自己的一套高性能內存池機制,用于避免頻繁 malloc/free:
-
ngx_pool_t
: 每個請求創建自己的內存池,生命周期跟隨請求 -
可申請小塊內存(<4096)快速分配
-
請求結束時統一釋放內存池,避免內存泄漏
連接相關資源也使用對象池(如 ngx_connection_t
、ngx_buf_t
)復用,提高性能。
7. 配置解析系統
Nginx 配置文件是模塊驅動的,結構清晰。
配置解析過程:
-
Master 進程讀取配置文件(
nginx.conf
) -
每個模塊注冊自己的配置指令及解析函數
-
創建配置結構體,填入配置參數
-
綁定到
ngx_cycle_t
中
支持嵌套結構、變量引用($host
)、include 等高級配置特性。
8.?零拷貝機制(Zero Copy)
為了提升性能,Nginx 避免數據拷貝,使用以下機制:
技術 | 說明 |
---|---|
sendfile() | 直接從磁盤文件到 socket,無需中轉緩沖區 |
mmap() | 映射文件到內存 |
writev() | 將多個緩沖區一次性寫入 socket |
splice() | 在 pipe 與 socket 之間傳輸,無需用戶態 |
這些機制顯著降低了 CPU 消耗和內存拷貝,提高了吞吐量。
9. 共享內存與緩存管理
Nginx 提供共享內存機制支持:
-
狀態統計模塊(如 stub_status)
-
緩存(proxy_cache, fastcgi_cache)
-
限流(limit_req, limit_conn)
-
Nginx Plus 狀態頁
通過 ngx_shm_zone_t
管理共享內存區域,使用 slab 分配器精細管理內存塊。
10. 日志系統
Nginx 日志系統也高度模塊化:
-
access_log
,error_log
-
格式化配置靈活:
log_format
-
支持異步寫入、緩存 flush、緩沖池等
-
每個請求獨立生成
ngx_log_t
對象
11. Nginx 底層架構圖示
┌────────────────────┐│ Master 進程 │└────────────────────┘│┌────────────┴────────────┐▼ ▼
┌─────────────┐ ┌─────────────┐
│ Worker 進程 │ ... │ Worker 進程 │
└─────────────┘ └─────────────┘│ epoll / kqueue / IOCP▼
┌──────────────────────────────────────┐
│ Reactor 模型:事件循環 │
├──────────────────────────────────────┤
│ Connection Pool + 內存池 + 模塊鏈 │
├──────────────────────────────────────┤
│ HTTP / TCP 模塊處理,輸出響應 │
└──────────────────────────────────────┘
12. 小結
關鍵模塊 | 技術點 |
---|---|
高性能 | 異步非阻塞 IO,sendfile |
高并發 | 多 worker,事件驅動 |
模塊化 | 所有功能模塊驅動 |
內存管理 | 內存池、連接池 |
高擴展性 | 插件化模塊架構 |
二、Nginx配置詳解并結合使用場景深入分析
1. 主配置(全局配置塊)
主配置一般出現在 nginx.conf
的最上層,用于控制整個服務器級別的行為。
常見配置項:
配置項 | 含義和作用 |
---|---|
user | 指定 worker 子進程運行的系統用戶 |
worker_processes | 啟動的 worker 數量,建議設置為 CPU 核心數 |
worker_cpu_affinity | 綁定 CPU,提高性能(用于多核) |
error_log | 全局錯誤日志路徑及級別(`debug |
pid | 指定 pid 文件路徑 |
worker_rlimit_nofile | 最大文件描述符限制 |
示例:
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
使用場景分析:
-
高并發服務:建議
worker_processes
設置為auto
,根據 CPU 自動適配。 -
Debug 排查問題時:可以臨時設置
error_log
為debug
。
2. events 塊配置(事件模型)
控制 Nginx 的事件處理機制,位于 nginx.conf
的 events
塊中。
常見配置項:
配置項 | 含義和作用 |
---|---|
worker_connections | 每個 worker 支持的最大連接數(非客戶端連接) |
use | 強制指定事件模型(如 epoll/kqueue) |
multi_accept | 是否每次盡可能多接受新連接 |
示例:
events {use epoll;worker_connections 10240;
}
使用場景分析:
-
高連接量網站:
worker_connections * worker_processes
決定最大并發連接數。 -
性能調優:使用
epoll
或kqueue
(操作系統支持前提下)可顯著提升性能。
3. http 塊配置(Web 服務核心)
這是 Nginx 配置中最重要的一部分,包含:
-
虛擬主機配置(
server
) -
路由與 location 規則
-
緩存、代理、gzip 等模塊
-
日志、限速、連接控制等功能
4. server 塊(虛擬主機配置)
一個 server 表示一個網站服務,可監聽不同端口/域名。
常見配置項:
配置項 | 含義和作用 |
---|---|
listen | 指定監聽的端口和地址 |
server_name | 指定虛擬主機域名 |
root | 指定 web 根目錄 |
index | 默認首頁文件 |
error_page | 自定義錯誤頁 |
示例:
server {listen 80;server_name www.example.com;root /var/www/example;index index.html index.htm;error_page 404 /404.html;
}
使用場景分析:
-
部署多個站點時,通過
server_name
和listen
實現虛擬主機劃分。 -
多端口監聽(如 80 和 443)可以寫多個
server
。
5. location 塊(請求路由匹配)
定義請求 URI 匹配規則和處理方式,是 HTTP 配置的核心。
匹配類型:
類型 | 示例 | 含義 |
---|---|---|
精確匹配 | location = /foo | 嚴格等于 /foo 時生效 |
前綴匹配 | location /img/ | URI 以 /img/ 開頭 |
正則匹配 | location ~ \.php$ | 匹配以 .php 結尾的路徑 |
示例:
location / {try_files $uri $uri/ =404;
}location /images/ {root /data;
}location ~ \.php$ {fastcgi_pass 127.0.0.1:9000;
}
使用場景分析:
-
靜態資源分流:不同類型資源設置不同緩存頭、路徑。
-
正則匹配動態請求,如 PHP、API 等。
6. 反向代理(proxy)
用于將客戶端請求轉發到后端服務器,是 Nginx 的核心能力之一。
常見配置項:
配置項 | 說明 |
---|---|
proxy_pass | 設置代理后端地址 |
proxy_set_header | 設置請求頭信息,傳遞客戶端信息 |
proxy_connect_timeout | 連接后端超時時間 |
proxy_read_timeout | 讀取響應超時時間 |
proxy_redirect | 修改后端跳轉地址 |
示例:
location /api/ {proxy_pass http://backend_server/api/;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;
}
使用場景分析:
-
微服務網關:按路徑路由不同服務。
-
解決跨域問題:配合
add_header
設置 CORS。
7.?負載均衡配置(upstream)
Nginx 作為七層負載均衡器,可配置多臺后端服務。
配置示例:
upstream backend {server 10.0.0.1:8080 weight=3;server 10.0.0.2:8080 max_fails=2 fail_timeout=30s;
}server {location / {proxy_pass http://backend;}
}
支持算法:
-
round-robin
(默認) -
least_conn
-
ip_hash
-
hash $request_uri consistent;
(需第三方模塊)
使用場景分析:
-
動態后端多副本:如服務部署多個 pod 實例,使用
upstream
配置調度。 -
實現后端故障自動摘除(
max_fails
,fail_timeout
)。
8. 限流與連接控制
請求限速:
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=1r/s;server {location /login/ {limit_req zone=req_limit burst=5;}
}
并發連接數限制:
limit_conn_zone $binary_remote_addr zone=addr:10m;server {location / {limit_conn addr 1;}
}
使用場景分析:
-
防止暴力破解、接口刷爆:對登錄、注冊接口限速。
-
限制單個 IP 并發連接,防止 DDoS。
9. 安全控制
拒絕某 IP:
deny 192.168.1.1;
allow 192.168.0.0/16;
強制 HTTPS:
server {listen 80;return 301 https://$host$request_uri;
}
設置頭防御:
add_header X-Frame-Options DENY;
add_header X-XSS-Protection "1; mode=block";
10. 緩存配置
靜態資源緩存:
location ~* \.(jpg|png|gif|css|js)$ {expires 30d;access_log off;
}
反向代理緩存(proxy_cache):
proxy_cache_path /tmp/nginx_cache levels=1:2 keys_zone=cache_zone:10m inactive=60m;location /api/ {proxy_cache cache_zone;proxy_pass http://backend;proxy_cache_valid 200 1h;
}
11. 日志配置
log_format main '$remote_addr - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log main;
使用場景分析:
-
業務分析:配合 ELK 分析用戶行為。
-
安全審計:統計惡意請求、慢請求等。
12. 常用變量(部分)
變量名 | 含義 |
---|---|
$remote_addr | 客戶端 IP |
$http_user_agent | 客戶端 UA |
$request_uri | 請求 URI |
$host | 請求的 Host 頭 |
$upstream_addr | 實際轉發到的后端地址(proxy) |
13. 小結:分類速查表
分類 | 功能 |
---|---|
主配置 | 進程管理、日志、用戶 |
events | IO 模型、最大連接數 |
http/server | 網站配置、虛擬主機 |
location | 路由控制、反向代理、限速 |
upstream | 負載均衡、服務池 |
cache | 緩存目錄、緩存策略 |
security | 黑名單、頭安全、重定向 |
logging | 日志格式與存儲路徑 |
三、Nginx如何解決跨域問題
1.?什么是跨域?為什么會出現跨域問題?
1.1 瀏覽器的同源策略(Same-Origin Policy)
同源策略要求:只有協議、域名、端口都相同,才能互相訪問資源。
http://example.com:80/api/user ?
http://example.com:8080/api/user ? (端口不同)
https://example.com/api/user ? (協議不同)
http://other.com/api/user ? (域名不同)
1.2 跨域行為觸發場景
當以下前端行為訪問非同源資源時,會觸發跨域限制:
-
使用
XMLHttpRequest
,fetch
,axios
發起請求 -
請求類型為非簡單請求(如
PUT
,DELETE
,application/json
) -
請求帶有自定義頭(如
Authorization
) -
頁面中嵌入
<iframe>
、<img>
、<script>
等外部域資源
2. 跨域類型與瀏覽器行為
2.1 簡單請求
滿足以下條件被認為是“簡單請求”:
-
方法為
GET
/POST
/HEAD
-
請求頭不超出:
-
Accept
,Accept-Language
,Content-Language
,Content-Type(必須是表單類型)
-
-
不帶
Authorization
、自定義 headers 等
瀏覽器行為:
? 直接發請求 + 檢查響應頭是否包含允許跨域的信息(如 Access-Control-Allow-Origin
)
2.2 非簡單請求(復雜請求)
如:
axios.post("http://api.server.com/data", data, {headers: { "Content-Type": "application/json" },
});
瀏覽器行為:
-
先發一條 OPTIONS 請求(預檢請求,Preflight)
-
服務端必須響應對應的 CORS 頭信息
-
如果 OPTIONS 返回成功,再發真正的業務請求
3. Nginx 解決跨域的原理與配置
Nginx 本身不受跨域限制(因為它不是瀏覽器),但它可以作為中間層,通過添加 CORS 響應頭或做代理來解決跨域。
4. Nginx 解決跨域的兩種方式
方式一:添加 CORS 響應頭(推薦)
場景:前端直接請求后端 API(后端已部署在 Nginx 上)
原理
添加以下 HTTP 響應頭,使瀏覽器信任該請求的跨源訪問:
Header 名稱 | 說明 |
---|---|
Access-Control-Allow-Origin | 允許的跨域源(* 或具體域名) |
Access-Control-Allow-Methods | 允許的請求方法(如 GET,POST,PUT ) |
Access-Control-Allow-Headers | 允許的請求頭(如 Authorization ) |
Access-Control-Allow-Credentials | 是否允許攜帶 Cookie(需具體域名,不能 * ) |
Access-Control-Max-Age | 預檢請求緩存時間(秒) |
Nginx 配置示例:
server {listen 80;server_name api.example.com;location /api/ {add_header Access-Control-Allow-Origin *;add_header Access-Control-Allow-Methods GET,POST,OPTIONS;add_header Access-Control-Allow-Headers Authorization,Content-Type;if ($request_method = OPTIONS ) {add_header Access-Control-Allow-Origin *;add_header Access-Control-Allow-Methods GET,POST,OPTIONS;add_header Access-Control-Allow-Headers Authorization,Content-Type;add_header Content-Length 0;add_header Content-Type text/plain;return 204;}proxy_pass http://backend_server;}
}
注意事項:
-
如果前端要帶 cookie,
Access-Control-Allow-Origin
不能是*
,必須指定具體域名,且加上:
add_header Access-Control-Allow-Credentials true;
方式二:通過 Nginx 反向代理解決跨域
場景:前端請求相同源的 Nginx,Nginx 再轉發請求至后端不同源
這種方式從瀏覽器的角度看“沒有跨域”,因為訪問的地址始終是前端部署在同源的 Nginx。
原理
通過 Nginx 配置,將 /api/*
的請求轉發到后端真實地址:
server {listen 80;server_name frontend.example.com;location /api/ {proxy_pass http://backend.example.com/;proxy_set_header Host $host;}
}
前端請求示例:
axios.get("/api/user");
-
前端以為在請求本域
/api/user
(不觸發 CORS) -
實際 Nginx 代理到
http://backend.example.com/user
? 這種方案徹底繞開瀏覽器同源限制,適用于內網 API 服務或多服務聚合。
5.?瀏覽器跨域判斷原理總結
請求行為 | 瀏覽器做什么 | 服務端需要響應什么 |
---|---|---|
簡單請求 | 直接發請求 | 返回 Access-Control-Allow-Origin |
非簡單請求 | 先發預檢(OPTIONS),再發正式請求 | 返回 Access-Control-Allow-Origin 、Methods、Headers 等 |
withCredentials | 跨域攜帶 Cookie | 響應頭中 Allow-Origin 不能是 * 且加 Allow-Credentials:true |
6. 實踐場景推薦方案
場景 | 推薦方式 | 是否支持 Cookie |
---|---|---|
前端直連后端 API(已配置 CORS) | 添加 CORS 響應頭 | 是 |
前端請求同域 Nginx,由其反代后端 | 反向代理 | 是 |
多服務統一網關接口聚合 | 網關反向代理統一出口 | 是 |
后端無法配置 CORS(第三方 API) | 通過 Nginx 轉發或服務中轉 | 是 |
7. 常見錯誤與排查建議
錯誤描述 | 可能原因 |
---|---|
No 'Access-Control-Allow-Origin' | 未設置 CORS 響應頭或設置不正確 |
OPTIONS 請求返回 404/405 | 后端或 Nginx 未正確處理預檢請求 |
cookie 不生效 | 使用了 * 而非具體域名;未設置 withCredentials |
Access-Control-Allow-Headers 缺失 | 設置了自定義頭,但未在響應中聲明允許 |
8. 補充:后端語言設置 CORS 的方式(參考)
后端語言 | 配置方式(示意) |
---|---|
Java (Spring Boot) | @CrossOrigin 或 CORS Filter |
Node.js (Express) | cors 中間件 |
Python Flask | flask-cors |
PHP | header('Access-Control-Allow-Origin: *') |
9. 小結
項目 | 原理 | Nginx 做法 |
---|---|---|
跨域限制 | 同源策略,瀏覽器攔截非同源請求 | 添加 CORS 響應頭 or 使用反向代理繞開 |
簡單請求 | 直接發請求,校驗響應頭 | 添加 Access-Control-Allow-Origin 等 |
非簡單請求 | 發起預檢(OPTIONS)再請求 | 對 OPTIONS 請求添加響應并返回 204 |
帶 Cookie | 需指定域名 + Allow-Credentials: true | Nginx 配置中同時設置域名和 credentials |
四、Nginx令牌桶限流
令牌桶(Token Bucket)算法是一種用于 限流(Rate Limiting) 的核心算法,廣泛用于:
-
接口限流(如 Nginx、網關、API Server)
-
網絡通信(如路由器 QoS)
-
服務熔斷和拒絕策略(如 Sentinel、Envoy)
1. 令牌桶算法的核心思想
? 定義
令牌桶算法通過以固定速率往桶中放令牌(token),請求來臨時從桶中取令牌,只有成功取到令牌的請求才被允許執行。
? 模型圖解:
+------------------+ 請求到達 +--------------------+
| 令牌定時器 |--------------------->| 嘗試從桶中取一個令牌 |
| 每隔 T 毫秒放 1 個 | | 若取成功 => 通過 |
| 令牌到桶中 | | 否則 => 拒絕/等待 |
+------------------+ +--------------------+
2. 核心參數
參數 | 含義 |
---|---|
rate (速率) | 每秒生成多少個令牌(如 10 token/sec) |
capacity (桶大小) | 桶中最多容納多少令牌(避免 burst 時過載) |
tokens | 當前桶中實際令牌數量 |
lastRefillTime | 上次放令牌的時間,用于計算應補充多少令牌 |
3. 請求流程(標準流程)
每當有一個請求到來:
-
刷新令牌數量:根據當前時間 -
lastRefillTime
計算該放入多少令牌。 -
更新令牌數:桶中現有令牌數 =
min(舊令牌 + 補充數, capacity)
-
檢查令牌是否足夠:
-
有令牌:取一個令牌,允許通過
-
無令牌:請求被限流(丟棄/等待/排隊等策略)
-
4. 代碼實現(簡化版本,Java 示例)
class TokenBucket {private final int capacity;private final int rate;private double tokens;private long lastRefillTimestamp;public TokenBucket(int capacity, int rate) {this.capacity = capacity;this.rate = rate;this.tokens = capacity;this.lastRefillTimestamp = System.currentTimeMillis();}public synchronized boolean allowRequest() {long now = System.currentTimeMillis();long deltaTime = now - lastRefillTimestamp;// 計算該生成多少新令牌double newTokens = deltaTime * rate / 1000.0;tokens = Math.min(capacity, tokens + newTokens);lastRefillTimestamp = now;if (tokens >= 1) {tokens -= 1;return true;} else {return false;}}
}
5. 與漏桶算法的對比
項目 | 令牌桶 Token Bucket | 漏桶 Leaky Bucket |
---|---|---|
控流方式 | 請求需要令牌,按速率放令牌 | 請求入隊,隊列以固定速率“漏出” |
允許突發請求 | ?(桶內有余令牌) | ?(請求被固定速率漏出) |
限制速率 | 平均速率受限,最大速率受桶容量限制 | 嚴格平滑速率輸出 |
典型用途 | Nginx 限流、API 限流、Redis 哨兵 | 視頻流輸出、網絡 QoS |
6. 令牌桶的使用場景分析
1. Nginx 限流(使用漏桶 + 令牌桶思想)
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=5r/s;location /api/ {limit_req zone=req_limit burst=10 nodelay;
}
-
rate=5r/s
:速率 = 5 個令牌/秒 -
burst=10
:桶容量 = 10,允許突發 10 次 -
nodelay
:有令牌立即放行(令牌桶特性)
本質是令牌桶思想:按速率補充令牌,有余量可突發請求。
2. 微服務 API 接口限流(如 Sentinel)
// 每秒最多允許 100 個請求
RateLimiter limiter = RateLimiter.create(100); // Guava 實現的令牌桶if (limiter.tryAcquire()) {// 執行請求邏輯
} else {// 拒絕或降級
}
7. 常見擴展策略
1. 令牌時間過期控制
-
可以為每個 token 附加 TTL,避免長時間積累失控
2. 按 IP/接口限流
-
key = client_ip
或key = user_id + path
,每個維度單獨維護一個桶
3. 集群令牌桶
-
分布式系統中,常結合 Redis 實現分布式令牌桶
8. 圖解理解令牌桶
? 時間軸演示(假設速率 5 r/s,容量 10)
t = 0s tokens = 10 (滿)
t = 0.2s 請求1來 => ?
t = 0.4s 請求2來 => ?
...
t = 1s tokens += 5
t = 1.2s 請求10來 => ?(用完 token)
t = 1.3s 請求11來 => ?(沒令牌)
9. 總結
關鍵點 | 說明 |
---|---|
控制平均速率 | 固定速率補充令牌,控制長期請求平均速率 |
支持突發流量 | 桶內積累的令牌可以一次性消耗,支持短時間高并發 |
高性能易實現 | 時間窗口 + 數學補償,無需復雜調度器 |
使用廣泛 | 應用于網關、限流器、API 請求控制、DDOS 防護等 |