一、概述
1.1 背景介紹
Nginx 的高性能源自其事件驅動架構。與 Apache 的"每連接一線程"模型不同,Nginx 使用單線程事件循環處理數千個并發連接。理解這套架構是調優的前提。
Nginx 采用 master-worker 進程模型。master 進程負責讀取配置、管理 worker 進程、綁定端口。實際的連接處理和請求處理由 worker 進程完成。每個 worker 進程運行一個事件循環,使用操作系統提供的 I/O 多路復用機制(Linux 上是 epoll)監聽所有連接上的事件,在單個線程內高效調度數千個連接。
epoll 的工作原理:
epoll 是 Linux 2.6 引入的 I/O 事件通知機制,相比 select/poll 有兩個核心優勢。第一,epoll 使用紅黑樹管理被監控的文件描述符,添加和刪除操作的時間復雜度是 O(log n),而 select/poll 每次調用都要將整個描述符集合從用戶空間復制到內核空間。第二,epoll 使用回調機制,內核在事件發生時將就緒的描述符加入就緒鏈表,epoll_wait 只需要檢查就緒鏈表,時間復雜度是 O(就緒事件數),而非 O(所有被監控的描述符數)。
在 10 萬并發連接的場景下,每次事件循環 select 需要遍歷 10 萬個描述符,而 epoll 只需要處理實際有事件的那幾十個描述符。這就是 Nginx 能在單個 worker 進程中處理數萬并發連接的根本原因。
連接處理的全鏈路分析:
一個 HTTP 請求從到達服務器到返回響應,經歷以下階段:
客戶端 → 內核網絡棧 → Nginx accept → 讀取請求 → 處理請求 → 代理到上游 → 上游響應 → 返回客戶端 具體到系統層面: 1. 數據包到達網卡,觸發硬中斷 2. 內核協議棧處理 TCP 三次握手,連接進入 accept 隊列 3. Nginx worker 的 epoll_wait 返回,調用 accept() 獲取新連接 4. 新連接的 fd 加入 epoll 監控 5. 數據到達,epoll 通知 worker 讀取請求 6. Nginx 解析 HTTP 請求,匹配 location 規則 7. 如果是反向代理,建立到 upstream 的連接 8. 將請求轉發到 upstream,等待響應 9. 從 upstream 讀取響應,通過緩沖區暫存 10. 將響應寫回客戶端連接 11. 如果是 keepalive 連接,保持連接等待下一個請求 12. 連接關閉,fd 從 epoll 中移除
并發瓶頸的四個層級:
高并發場景下的瓶頸不會只出現在 Nginx 配置層面。完整的瓶頸分析需要覆蓋四個層級:
系統層瓶頸:文件描述符數量限制(每個連接消耗一個 fd)、內核網絡棧參數(TCP backlog、端口范圍、連接跟蹤表)、內存限制(每個連接需要內核和用戶空間各分配緩沖區)。
Nginx 層瓶頸:worker 進程數、每個 worker 的最大連接數、事件處理模型配置、keepalive 參數、緩沖區大小。
上游層瓶頸:到 upstream 的連接池配置、upstream keepalive 連接數、upstream 響應時間、連接超時設置。
應用層瓶頸:后端應用的處理能力(QPS 上限)、數據庫連接池、緩存命中率、應用線程池/協程池大小。
Nginx 調優的核心思路是:確保每一層都不會成為瓶頸,讓請求能夠順暢地通過整條鏈路。
1.2 核心概念
連接與請求的區別:
一個 TCP 連接(connection)上可以承載多個 HTTP 請求(request)。HTTP/1.1 的 keepalive 機制允許在同一個連接上串行發送多個請求。HTTP/2 更進一步,在同一個連接上可以并行發送多個請求(通過多路復用 stream)。因此 Nginx 的 worker_connections 參數限制的是并發連接數而非并發請求數。
反向代理場景的連接消耗:
當 Nginx 作為反向代理時,每個客戶端請求需要消耗兩個連接:一個是客戶端到 Nginx 的前端連接,一個是 Nginx 到 upstream 的后端連接。因此實際能處理的并發客戶端請求數約為 worker_connections / 2。如果啟用了 upstream keepalive 連接池,后端連接可以復用,實際能處理的并發數會高于這個理論值。
背壓(Backpressure)機制:
Nginx 的 proxy_buffer 機制實現了上游和下游之間的背壓解耦。當 upstream 的響應速度快于客戶端的接收速度時,Nginx 將響應緩存在 proxy_buffer 中,盡快釋放 upstream 連接。如果緩沖區也滿了,Nginx 會暫停從 upstream 讀取數據,形成自然的背壓。這個機制使得一個慢客戶端不會長時間占用 upstream 的連接資源。
1.3 適用場景
Web 服務器/反向代理在高并發下出現 502/504 錯誤
服務器連接數達到上限,新請求被拒絕(Connection refused)
TIME_WAIT 連接堆積導致端口耗盡
upstream 響應正常但客戶端體感延遲高
壓測時 QPS 無法隨資源線性增長
HTTP/2 或 HTTP/3 環境下的特定調優需求
1.4 環境要求
| 組件 | 版本要求 | 說明 |
|---|---|---|
| 操作系統 | Ubuntu 24.04 LTS / Rocky Linux 9.5 | 內核 6.12+ |
| Linux 內核 | 6.12+ | 支持 io_uring、BBR v3、最新 TCP 優化 |
| Nginx | 1.27.x | 主線版本,包含最新特性 |
| OpenSSL | 3.3+ | TLS 1.3 和 HTTP/3 (QUIC) 支持 |
| systemd | 256+ | 進程管理和資源限制 |
| wrk | 4.2+ | HTTP 壓測工具 |
| vegeta | 12.12+ | HTTP 負載測試工具 |
| Prometheus | 3.x | 監控指標采集 |
| Grafana | 11.x | 監控儀表盤 |
| nginx-module-vts 或 nginx-prometheus-exporter | 最新版 | Nginx 指標暴露 |
二、詳細步驟
2.1 系統層調優
2.1.1 文件描述符限制
每個網絡連接消耗一個文件描述符。如果要支持 10 萬并發連接,Nginx worker 進程需要能打開至少 10 萬個文件描述符。
# 查看系統級別的文件描述符限制 cat /proc/sys/fs/file-max # 默認值通常足夠大(數十萬到數百萬),但需要確認 # 查看系統當前已使用的文件描述符數 cat /proc/sys/fs/file-nr # 輸出格式:已分配 空閑 最大值 # 查看 Nginx 進程的文件描述符限制 # 先獲取 worker 進程 PID ps aux | grep"nginx: worker" # 再查看限制 cat /proc//limits | grep"Max open files"
調整文件描述符限制:
# 方法 1:通過 sysctl 調整系統級限制 cat <'EOF'?| sudo tee /etc/sysctl.d/99-nginx-tuning.conf # 系統級最大文件描述符數 fs.file-max = 2097152 # inotify 監控實例數(與 Nginx 無直接關系,但大量文件監控場景需要) fs.inotify.max_user_instances = 8192 fs.inotify.max_user_watches = 524288 EOF sudo sysctl --system # 方法 2:通過 limits.conf 調整用戶級限制 cat <'EOF'?| sudo tee /etc/security/limits.d/99-nginx.conf # Nginx 運行用戶的文件描述符限制 nginx ? ?soft ? ?nofile ? ?1048576 nginx ? ?hard ? ?nofile ? ?1048576 root ? ? soft ? ?nofile ? ?1048576 root ? ? hard ? ?nofile ? ?1048576 * ? ? ? ?soft ? ?nofile ? ?1048576 * ? ? ? ?hard ? ?nofile ? ?1048576 EOF # 方法 3:通過 systemd 單元配置(推薦,精準控制 Nginx 進程) sudo mkdir -p /etc/systemd/system/nginx.service.d/ cat <'EOF'?| sudo tee /etc/systemd/system/nginx.service.d/limits.conf [Service] LimitNOFILE=1048576 EOF sudo systemctl daemon-reload sudo systemctl restart nginx # 驗證生效 cat /proc/$(pgrep -f?"nginx: master")/limits | grep?"Max open files" # 預期輸出:Max open files ? ? ? ? ? ?1048576 ? ? ? ? ? ? ?1048576 ? ? ? ? ? ? ?files
2.1.2 內核網絡參數調優
# 高并發 Nginx 服務器的內核參數調優 cat <'EOF'?| sudo tee /etc/sysctl.d/99-nginx-network.conf # ============ TCP 連接相關 ============ # TCP 全連接隊列長度(accept 隊列) # 當 Nginx accept() 來不及處理時,新連接在這里排隊 # 默認 128,高并發場景遠遠不夠 net.core.somaxconn = 65535 # TCP SYN 半連接隊列長度 # 三次握手過程中等待 ACK 的連接在這里排隊 net.ipv4.tcp_max_syn_backlog = 65535 # 允許 TIME_WAIT 狀態的 socket 被快速回收復用 # 只影響連接到外部服務器的連接(Nginx 到 upstream) net.ipv4.tcp_tw_reuse = 1 # 本地端口范圍(Nginx 連接 upstream 時需要本地端口) # 默認 32768-60999,約 2.8 萬個端口 # 擴大到 1024-65535,約 6.4 萬個端口 net.ipv4.ip_local_port_range = 1024 65535 # TCP FIN_WAIT2 超時時間(秒) # 默認 60 秒,高并發下可以適當縮短 net.ipv4.tcp_fin_timeout = 15 # TCP keepalive 參數(系統默認值,Nginx 有自己的 keepalive 配置) net.ipv4.tcp_keepalive_time = 600 net.ipv4.tcp_keepalive_intvl = 30 net.ipv4.tcp_keepalive_probes = 3 # ============ 緩沖區相關 ============ # 網絡設備的接收隊列長度 net.core.netdev_max_backlog = 65535 # 默認和最大的 socket 緩沖區大小 net.core.rmem_default = 262144 net.core.wmem_default = 262144 net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 # TCP 緩沖區自動調整范圍:最小值 ?默認值 ?最大值(字節) net.ipv4.tcp_rmem = 4096 131072 16777216 net.ipv4.tcp_wmem = 4096 131072 16777216 # 系統總的 TCP 內存頁數限制 net.ipv4.tcp_mem = 786432 1048576 1572864 # ============ 擁塞控制 ============ # 啟用 BBR 擁塞控制算法(Linux 4.9+) net.core.default_qdisc = fq net.ipv4.tcp_congestion_control = bbr # ============ 連接跟蹤 ============ # 連接跟蹤表大小(如果使用了 iptables/nftables) # 每個連接跟蹤條目約占 300 字節 # 10 萬連接需要約 30MB 內存 net.netfilter.nf_conntrack_max = 1048576 net.netfilter.nf_conntrack_tcp_timeout_established = 600 net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30 # ============ 其他 ============ # 允許更多的 SYN 重試 net.ipv4.tcp_synack_retries = 2 # 啟用 TCP 快速打開(TFO),減少握手延遲 net.ipv4.tcp_fastopen = 3 # 增大 ARP 緩存 net.ipv4.neigh.default.gc_thresh1 = 4096 net.ipv4.neigh.default.gc_thresh2 = 8192 net.ipv4.neigh.default.gc_thresh3 = 16384 EOF sudo sysctl --system # 驗證關鍵參數 sysctl net.core.somaxconn net.ipv4.tcp_max_syn_backlog ? ? net.ipv4.ip_local_port_range net.ipv4.tcp_congestion_control
2.1.3 驗證 BBR 擁塞控制
# 確認 BBR 模塊已加載 lsmod | grep bbr # 如果沒有加載 sudo modprobe tcp_bbr # 確認當前使用的擁塞控制算法 sysctl net.ipv4.tcp_congestion_control # 預期輸出:net.ipv4.tcp_congestion_control = bbr # 查看可用的擁塞控制算法 sysctl net.ipv4.tcp_available_congestion_control # BBR 相比 CUBIC 的優勢: # - 基于帶寬和 RTT 估計而非丟包率 # - 在高延遲和有損網絡中表現更好 # - 能更好地利用可用帶寬 # - 對 buffer 膨脹(bufferbloat)有更好的抑制
2.2 Nginx 全局參數調優
2.2.1 worker 進程配置
# 文件路徑:/etc/nginx/nginx.conf # worker 進程數 # auto 會根據 CPU 核心數自動設置 # 在純代理場景下設為 CPU 核心數 # 如果有大量 CPU 密集操作(如 SSL 握手、gzip 壓縮),可以設為核心數的 1-2 倍 worker_processes auto; # 綁定 worker 進程到特定 CPU 核心 # 減少 CPU 緩存失效和上下文切換 # auto 自動分配(Nginx 1.9.10+) worker_cpu_affinity auto; # 每個 worker 進程的最大文件描述符數 # 必須大于等于 worker_connections worker_rlimit_nofile 1048576; # 錯誤日志級別(高并發下 warn 或 error 即可,info/debug 會產生大量日志) error_log /var/log/nginx/error.log warn; # PID 文件 pid /run/nginx.pid;
2.2.2 事件模型配置
events {
# 每個 worker 的最大并發連接數
# 這個數字包含所有連接:客戶端連接 + upstream 連接 + 本地文件 fd
# 反向代理場景:可服務的并發客戶端數 ≈ worker_connections / 2
# 10 萬并發需要:worker_connections >= 200000 / worker_processes
worker_connections 65535;
# 使用 epoll 事件模型(Linux 默認,顯式指定更清晰)
use epoll;
# 允許一個 worker 一次 accept 多個連接
# 減少 epoll_wait 的調用次數,提高 accept 效率
multi_accept on;
# accept 互斥鎖
# off:所有 worker 都監聽同一個端口,由內核調度(reuseport 更優)
# Nginx 1.11.3+ 配合 reuseport 時建議關閉
accept_mutex off;
}
reuseport 優化:
# 在 listen 指令中啟用 reuseport
# reuseport 讓每個 worker 進程擁有獨立的 listen socket
# 內核在 TCP 層面做負載均衡,避免驚群效應
server {
listen 80 reuseport;
listen 443 ssl reuseport;
# ...
}
# reuseport 的效果:
# - 消除 accept_mutex 鎖競爭
# - 內核級別的 worker 間負載均衡
# - 在高并發下(>10000 連接/秒)提升 2-3 倍的新建連接能力
2.2.3 HTTP 核心配置
http {
# ============ 基礎配置 ============
# MIME 類型
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 日志格式(高并發下日志 I/O 也是瓶頸之一)
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time '
'$connection $connection_requests';
# 訪問日志緩沖寫入(減少 I/O 次數)
access_log /var/log/nginx/access.log main buffer=64k flush=5s;
# 高并發壓測時可以臨時關閉訪問日志
# access_log off;
# ============ 性能配置 ============
# 使用 sendfile 系統調用傳輸文件(零拷貝)
sendfile on;
# 配合 sendfile 使用,將多個小數據塊合并為一個 TCP 包發送
tcp_nopush on;
# 禁用 Nagle 算法,減少小數據包的延遲
# 與 tcp_nopush 配合:先用 nopush 攢數據,最后一個包用 nodelay 立即發送
tcp_nodelay on;
# ============ 連接管理 ============
# 客戶端 keepalive 超時時間(秒)
# 連接空閑超過此時間后關閉
# 高并發下不宜設太長,否則空閑連接占用過多 fd
keepalive_timeout 65;
# 單個 keepalive 連接上允許的最大請求數
# 達到后關閉連接,防止單個連接長期占用資源
keepalive_requests 1000;
# keepalive 連接的空閑超時(僅 Nginx 1.19.10+)
# 與 keepalive_timeout 區別:這個是 upstream keepalive 的參數
# keepalive_time 1h;
# ============ 超時配置 ============
# 客戶端請求體讀取超時(秒)
client_body_timeout 30;
# 客戶端請求頭讀取超時(秒)
client_header_timeout 15;
# 向客戶端發送響應的超時(兩次 write 操作之間的間隔)
send_timeout 30;
# ============ 緩沖區配置 ============
# 客戶端請求體的緩沖區大小
client_body_buffer_size 16k;
# 客戶端請求頭的緩沖區大小
client_header_buffer_size 4k;
# 大請求頭的緩沖區
large_client_header_buffers 4 16k;
# 最大請求體大?。ㄎ募蟼飨拗疲? client_max_body_size 100m;
# ============ 壓縮配置 ============
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 4;
gzip_min_length 1000;
gzip_types text/plain text/css application/json application/javascript
text/xml application/xml application/xml+rss text/javascript
image/svg+xml;
# ============ 連接關閉控制 ============
# 延遲關閉(lingering close)
# 確保在關閉連接前讀完客戶端發送的剩余數據
# 防止客戶端收到 RST 導致的數據丟失
lingering_close on;
lingering_time 30s;
lingering_timeout 5s;
# 對超時連接發送 RST 而非 FIN
# 減少 TIME_WAIT 狀態的連接數
# 僅對超時連接生效,正常連接仍走優雅關閉
reset_timedout_connection on;
# 包含其他配置文件
include /etc/nginx/conf.d/*.conf;
}
2.3 上游連接池配置
upstream keepalive 是高并發調優中最容易被忽略但效果最顯著的配置項。
# upstream 配置示例
upstream backend {
# 后端服務器列表
server 10.0.1.10:8080 max_fails=3 fail_timeout=30s;
server 10.0.1.11:8080 max_fails=3 fail_timeout=30s;
server 10.0.1.12:8080 max_fails=3 fail_timeout=30s;
# keepalive 連接池大小
# 每個 worker 進程保持的到 upstream 的空閑 keepalive 連接數
# 這不是最大連接數,而是"空閑連接緩存"大小
# 當請求量超過 keepalive 數時,多余的連接在使用完后會被關閉
#
# 計算方法:
# keepalive >= 平均并發請求數 / worker_processes
# 但不宜設太大,否則空閑連接占用過多服務端資源
keepalive 64;
# 每個 keepalive 連接上的最大請求數(Nginx 1.15.3+)
# 達到后連接被關閉并重新建立
keepalive_requests 10000;
# keepalive 連接的最大存活時間(Nginx 1.19.10+)
# 防止長時間復用同一個連接
keepalive_time 1h;
# keepalive 連接空閑超時(Nginx 1.15.3+)
# 空閑超過此時間的連接會被回收
keepalive_timeout 60s;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://backend;
# 使用 HTTP/1.1 到 upstream(keepalive 需要 HTTP/1.1)
proxy_http_version 1.1;
# 清除 Connection 頭,防止客戶端的 "Connection: close" 傳遞到 upstream
proxy_set_header Connection "";
# 超時配置
proxy_connect_timeout 5s; # 連接 upstream 的超時
proxy_send_timeout 30s; # 發送請求到 upstream 的超時
proxy_read_timeout 60s; # 等待 upstream 響應的超時
# 代理緩沖區配置
proxy_buffering on;
proxy_buffer_size 8k; # 響應頭的緩沖區
proxy_buffers 32 16k; # 響應體的緩沖區(32 個 16k 的塊)
proxy_busy_buffers_size 64k; # 在響應還未完全讀取時可以發送給客戶端的緩沖區大小
# 請求頭傳遞
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
upstream keepalive 未配置時的問題:
# 沒有 keepalive 時,每個請求到 upstream 都會新建 TCP 連接 # 每次新建連接:TCP 三次握手(至少 1.5 RTT)+ 內核分配資源 # 大量短連接導致 TIME_WAIT 堆積 # 查看 TIME_WAIT 連接數 ss -s | grep"TIME-WAIT" # 或 ss -tn state time-wait | wc -l # 查看到 upstream 的 TIME_WAIT ss -tn state time-wait dst 10.0.1.10 ss -tn state time-wait dst 10.0.1.11 # 如果 TIME_WAIT 數量超過本地端口范圍(默認約 28000 個端口) # 新連接會因為無法分配源端口而失敗 # 表現為 Nginx 報 502 錯誤,日志中出現 "cannot assign requested address"
2.4 限流限速配置
http {
# ============ 連接數限制 ============
# 定義限制區域(基于客戶端 IP)
# zone=conn_limit:10m 表示使用 10MB 共享內存存儲狀態
# 每個 IP 狀態約占 64 字節,10MB 可以跟蹤約 16 萬個 IP
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
# 基于服務名的連接數限制
limit_conn_zone $server_name zone=server_conn_limit:10m;
# ============ 請求速率限制 ============
# 定義速率限制區域
# rate=100r/s 表示每秒 100 個請求
limit_req_zone $binary_remote_addr zone=req_limit:20m rate=100r/s;
# API 接口的速率限制(更嚴格)
limit_req_zone $binary_remote_addr zone=api_limit:20m rate=30r/s;
server {
listen 80;
# 每個 IP 最多 200 個并發連接
limit_conn conn_limit 200;
# 每個 server 最多 50000 個并發連接
limit_conn server_conn_limit 50000;
# 限流錯誤碼(默認 503)
limit_conn_status 429;
limit_req_status 429;
location / {
# 請求速率限制
# burst=200 允許突發 200 個請求進入隊列
# nodelay 立即處理突發請求(不延遲排隊)
limit_req zone=req_limit burst=200 nodelay;
proxy_pass http://backend;
}
location /api/ {
# API 接口更嚴格的速率限制
limit_req zone=api_limit burst=50 nodelay;
proxy_pass http://backend;
}
}
}
2.5 HTTP/2 調優
server {
listen 443 ssl http2 reuseport;
server_name www.example.com;
# SSL 證書
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
# SSL 優化
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256ECDHE-ECDSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers off;
# SSL 會話緩存(減少 TLS 握手次數)
ssl_session_cache shared50m;
ssl_session_timeout 1d;
ssl_session_tickets on;
# OCSP Stapling(減少客戶端的證書驗證延遲)
ssl_stapling on;
ssl_stapling_verify on;
# HTTP/2 特定參數
# 單連接上的最大并發 stream 數
http2_max_concurrent_streams 128;
# HTTP/2 連接的最大請求數
keepalive_requests 10000;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
2.6 HTTP/3 (QUIC) 配置
# Nginx 1.25.0+ 原生支持 HTTP/3
# 需要使用 --with-http_v3_module 編譯(或使用官方預編譯包)
server {
# HTTP/3 使用 UDP 協議
listen 443 quic reuseport;
# HTTP/2 兼容
listen 443 ssl http2;
server_name www.example.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
# HTTP/3 需要 TLS 1.3
ssl_protocols TLSv1.2 TLSv1.3;
# 通知客戶端支持 HTTP/3
add_header Alt-Svc 'h3=":443"; ma=86400';
# QUIC 特定參數
# quic_retry on; # 啟用地址驗證(防止 DDoS)
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
# 驗證 HTTP/3 支持 curl --http3 https://www.example.com/ -I # 如果 curl 不支持 HTTP/3,使用 h3 測試工具 # 或在瀏覽器開發者工具中查看 Protocol 列
2.7 啟動和驗證
# 檢查 Nginx 配置語法
sudo nginx -t
# 預期輸出:
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful
# 查看編譯參數(確認支持的模塊)
nginx -V 2>&1 | tr' ''
'| grep -E"^--with"
# 重載配置(不中斷服務)
sudo nginx -s reload
# 驗證 worker 進程數
ps aux | grep"nginx: worker"| grep -v grep | wc -l
# 驗證 worker 的文件描述符限制
forpidin$(pgrep -f"nginx: worker");do
echo"PID$pid:$(cat /proc/$pid/limits | grep 'Max open files' | awk '{print $5}')"
done
# 驗證監聽端口
ss -tlnp | grep nginx
# 驗證 reuseport 是否生效(應該看到每個 worker 都有獨立的 listen socket)
ss -tlnp | grep":80"| wc -l
# 如果啟用了 reuseport,數量應等于 worker 進程數
# 驗證 keepalive 配置
curl -v -H"Connection: keep-alive"http://localhost/ 2>&1 | grep -i"keep-alive"
# 快速壓測驗證
# 使用 wrk 進行基準測試
wrk -t4 -c1000 -d30s http://localhost/
三、示例代碼和配置
3.1 完整配置示例
3.1.1 高并發反向代理完整配置
# 文件路徑:/etc/nginx/nginx.conf
# 適用場景:10 萬級并發的反向代理服務器
# 硬件規格參考:8 核 16GB,千兆網絡
user nginx;
worker_processes auto;
worker_cpu_affinity auto;
worker_rlimit_nofile 1048576;
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;
events {
worker_connections 65535;
use epoll;
multi_accept on;
accept_mutex off;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 日志格式
log_format main '$remote_addr - [$time_local] '
'"$request" $status $body_bytes_sent '
'rt=$request_time uct=$upstream_connect_time '
'urt=$upstream_response_time '
'conn=$connection reqs=$connection_requests';
# 日志緩沖寫入
access_log /var/log/nginx/access.log main buffer=64k flush=5s;
# 基礎性能
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# 連接管理
keepalive_timeout 65;
keepalive_requests 1000;
reset_timedout_connection on;
# 超時
client_body_timeout 30;
client_header_timeout 15;
send_timeout 30;
# 緩沖區
client_body_buffer_size 16k;
client_header_buffer_size 4k;
large_client_header_buffers 4 16k;
client_max_body_size 50m;
# 壓縮
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 4;
gzip_min_length 1000;
gzip_types text/plain text/css application/json application/javascript
text/xml application/xml text/javascript image/svg+xml;
# 限流
limit_conn_zone $binary_remote_addr zone=conn_per_ip:10m;
limit_req_zone $binary_remote_addr zone=req_per_ip:20m rate=200r/s;
limit_conn_status 429;
limit_req_status 429;
# upstream 定義
upstream app_backend {
server 10.0.1.10:8080 max_fails=3 fail_timeout=30s weight=5;
server 10.0.1.11:8080 max_fails=3 fail_timeout=30s weight=5;
server 10.0.1.12:8080 max_fails=3 fail_timeout=30s weight=3;
keepalive 128;
keepalive_requests 10000;
keepalive_timeout 60s;
}
# 默認 server(拒絕未匹配的請求)
server {
listen 80 default_server;
server_name _;
return 444;
}
# 主站點
server {
listen 80 reuseport;
server_name www.example.com api.example.com;
# 連接限制
limit_conn conn_per_ip 300;
# 狀態監控端點
location /nginx_status {
stub_status on;
allow 10.0.0.0/8;
deny all;
}
# 健康檢查端點
location /health {
access_log off;
return 200 "OK
";
}
# 靜態文件
location /static/ {
alias /var/www/static/;
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
# API 代理
location /api/ {
limit_req zone=req_per_ip burst=100 nodelay;
proxy_pass http://app_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_connect_timeout 5s;
proxy_send_timeout 30s;
proxy_read_timeout 60s;
proxy_buffering on;
proxy_buffer_size 8k;
proxy_buffers 32 16k;
proxy_busy_buffers_size 64k;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 錯誤處理:upstream 返回錯誤時嘗試下一個服務器
proxy_next_upstream error timeout http_502 http_503;
proxy_next_upstream_tries 2;
proxy_next_upstream_timeout 10s;
}
# 默認代理
location / {
limit_req zone=req_per_ip burst=200 nodelay;
proxy_pass http://app_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_connect_timeout 5s;
proxy_send_timeout 30s;
proxy_read_timeout 60s;
proxy_buffering on;
proxy_buffer_size 8k;
proxy_buffers 16 16k;
proxy_busy_buffers_size 32k;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}
3.2 實際應用案例
案例一:worker_connections 不足導致 502
場景描述:某電商平臺的 Nginx 反向代理在促銷活動期間頻繁返回 502 錯誤。Nginx error.log 中出現大量 "worker_connections are not enough" 警告。后端應用服務器負載正常,響應時間也正常。
排查過程:
# 查看 Nginx 錯誤日志 tail -100 /var/log/nginx/error.log | grep -i"connections" # 2026/03/13 1401 [alert] 1234#1234: 768 worker_connections are not enough # 查看當前 Nginx 配置 grep worker_connections /etc/nginx/nginx.conf # worker_connections 768; <- 默認值太小 # 查看 worker 進程數 grep worker_processes /etc/nginx/nginx.conf # worker_processes 4; # 當前最大并發連接數 = 4 * 768 = 3072 個連接 # 反向代理每個請求消耗 2 個連接,實際只能服務約 1536 個并發請求 # 查看當前實際連接數 ss -s # TCP: ? 5432 (estab 4210, closed 890, orphaned 12, timewait 890) # 查看 Nginx 連接狀態 curl -s http://localhost/nginx_status # Active connections: 3891 # server accepts handled requests # ?2847362 2847362 15823451 # Reading: 42 Writing: 312 Waiting: 3537
解決方案:
# 第一步:調整 worker_connections # 編輯 /etc/nginx/nginx.conf # 將 worker_connections 從 768 調整到 65535 # 第二步:同時調整系統層限制 # 文件描述符 cat <'EOF'?| sudo tee /etc/systemd/system/nginx.service.d/limits.conf [Service] LimitNOFILE=1048576 EOF # 內核參數 echo"net.core.somaxconn = 65535"?| sudo tee -a /etc/sysctl.d/99-nginx.conf sudo sysctl --system # 第三步:在 nginx.conf 中添加 # worker_rlimit_nofile 1048576; # 第四步:重載配置 sudo systemctl daemon-reload sudo nginx -t && sudo nginx -s reload # 第五步:驗證 # 新的最大并發:4 * 65535 = 262140 個連接 # 反向代理模式:約 131070 個并發請求 cat /proc/$(pgrep -f?"nginx: worker"?| head -1)/limits | grep?"Max open files"
案例二:upstream keepalive 未配置導致 TIME_WAIT 堆積
場景描述:Nginx 反向代理服務器在高峰期頻繁出現 502 錯誤,error.log 中記錄 "cannot assign requested address while connecting to upstream"。后端服務正常,但 Nginx 服務器上有數萬個 TIME_WAIT 連接。
排查過程:
# 查看 TIME_WAIT 連接數
ss -s | grep TIME-WAIT
# TCP: 48291 (estab 3200, closed 2100, orphaned 5, timewait 43002)
# 查看到 upstream 的 TIME_WAIT
ss -tn state time-wait dst 10.0.1.10 | wc -l
# 14523
ss -tn state time-wait dst 10.0.1.11 | wc -l
# 14891
ss -tn state time-wait dst 10.0.1.12 | wc -l
# 13588
# 查看本地端口范圍
sysctl net.ipv4.ip_local_port_range
# net.ipv4.ip_local_port_range = 32768 60999
# 可用端口約 28000 個
# 每個目標 IP 的 TIME_WAIT 已接近端口范圍上限
# 查看 Nginx upstream 配置
grep -A 10"upstream"/etc/nginx/conf.d/default.conf
# upstream backend {
# server 10.0.1.10:8080;
# server 10.0.1.11:8080;
# server 10.0.1.12:8080;
# }
# 沒有 keepalive 配置,每個請求都新建 TCP 連接到 upstream
# 查看 proxy_http_version
grep"proxy_http_version"/etc/nginx/conf.d/default.conf
# 沒有設置,默認是 HTTP/1.0,不支持 keepalive
解決方案:
# 第一步:添加 upstream keepalive
# 修改 upstream 配置
# upstream backend {
# server 10.0.1.10:8080;
# server 10.0.1.11:8080;
# server 10.0.1.12:8080;
# keepalive 128;
# keepalive_requests 10000;
# keepalive_timeout 60s;
# }
# 第二步:在 location 中啟用 HTTP/1.1 和清除 Connection 頭
# location / {
# proxy_pass http://backend;
# proxy_http_version 1.1;
# proxy_set_header Connection "";
# ...
# }
# 第三步:擴大本地端口范圍(兜底措施)
echo"net.ipv4.ip_local_port_range = 1024 65535"|
sudo tee -a /etc/sysctl.d/99-nginx.conf
# 第四步:啟用 TIME_WAIT 復用
echo"net.ipv4.tcp_tw_reuse = 1"|
sudo tee -a /etc/sysctl.d/99-nginx.conf
sudo sysctl --system
# 第五步:重載 Nginx
sudo nginx -t && sudo nginx -s reload
# 驗證效果(觀察 TIME_WAIT 數量變化)
watch -n 5'ss -s | grep TIME-WAIT'
# 幾分鐘后 TIME_WAIT 應該逐漸降低
# 配置 keepalive 后的效果:
# 連接被復用,不再頻繁新建和關閉
# TIME_WAIT 從 43000 降到 2000 以下
# 502 錯誤消失
案例三:10 萬并發壓測調優全過程
場景描述:需要對一個新部署的 Nginx 反向代理進行 10 萬并發連接的壓力測試。服務器配置為 16 核 32GB,萬兆網卡。后端有 8 臺應用服務器。目標是支持 10 萬并發連接,QPS 達到 5 萬以上。
調優過程:
# ============ 第一輪:基準測試 ============ # 使用默認配置進行基準測試 wrk -t8 -c10000 -d60s http://target-server/api/health # 基準結果(模擬數據): # Running 1m test @ http://target-server/api/health # 8 threads and 10000 connections # Thread Stats Avg Stdev Max +/- Stdev # Latency 42.3ms 15.2ms 312ms 78.5% # Req/Sec 3125 412 4501 72.1% # 1498762 requests in 1m, 285MB read # Socket errors: connect 6752, read 0, write 0, timeout 234 # Requests/sec: 24979.37 # 問題分析: # 1. 大量連接錯誤(connect 6752)—— 連接數限制不夠 # 2. QPS 約 2.5 萬,距離目標 5 萬還有差距 # 3. 有超時錯誤(timeout 234) # ============ 第二輪:系統層調優 ============ # 應用前面的系統層優化 sudo sysctl -p /etc/sysctl.d/99-nginx-tuning.conf sudo sysctl -p /etc/sysctl.d/99-nginx-network.conf # 調整 Nginx 文件描述符限制 sudo mkdir -p /etc/systemd/system/nginx.service.d/ cat <'EOF'?| sudo tee /etc/systemd/system/nginx.service.d/limits.conf [Service] LimitNOFILE=1048576 EOF sudo systemctl daemon-reload # 再次測試 wrk -t8 -c10000 -d60s http://target-server/api/health # 第二輪結果: # Socket errors: connect 0, read 0, write 0, timeout 12 # Requests/sec: ?31245.89 # 連接錯誤消失,QPS 提升到 3.1 萬 # ============ 第三輪:Nginx 配置調優 ============ # 調整 worker_connections、添加 reuseport、配置 upstream keepalive # 使用前面的完整配置模板 sudo nginx -t && sudo nginx -s reload # 提高并發到 5 萬 wrk -t16 -c50000 -d60s http://target-server/api/health # 第三輪結果: # Requests/sec: ?48923.17 # 平均延遲:25.6ms # 無連接錯誤 # QPS 接近 5 萬 # ============ 第四輪:精細調優 ============ # 1. 確認壓測客戶端本身不是瓶頸(使用多臺客戶端) # 2. 檢查 Nginx 的 CPU 使用率分布 pidstat -p $(pgrep -f?"nginx: worker"?| tr?' '',') 1 10 # 如果某個 worker CPU 使用率明顯高于其他,說明負載不均衡 # 啟用 reuseport 后應該比較均衡 # 3. 檢查網絡瓶頸 sar -n DEV 1 10 # 查看網卡帶寬使用率 # 4. 提高到 10 萬并發 wrk -t16 -c100000 -d120s http://target-server/api/health # 最終結果(模擬數據): # 16 threads and 100000 connections # Thread Stats ? Avg ? ? ?Stdev ? ? Max ? +/- Stdev # ? Latency ? ?35.8ms ? 18.4ms ? 520ms ? ?82.3% # ? Req/Sec ? ?3312 ? ? 578 ? ? ? 4892 ? ? 68.7% # 6350421 requests in 2m # Requests/sec: ?52920.17 # 無連接錯誤,無超時錯誤 # 目標達成
3.2.1 壓測與調優自動化腳本
#!/bin/bash # 文件名:nginx_benchmark.sh # 功能:Nginx 性能基準測試腳本 # 依賴:wrk, curl, ss # 用法:./nginx_benchmark.sh[connections] [duration] set-euo pipefail # ============ 參數 ============ TARGET_URL="${1:?用法: $0 [connections] [duration]}" CONNECTIONS="${2:-1000}" DURATION="${3:-30s}" THREADS=$(nproc) REPORT_DIR="/tmp/nginx_benchmark_$(date +%Y%m%d_%H%M%S)" mkdir -p"$REPORT_DIR" echo"========================================" echo"Nginx 性能基準測試" echo"========================================" echo"目標 URL:$TARGET_URL" echo"并發連接:$CONNECTIONS" echo"測試時長:$DURATION" echo"線程數: $THREADS" echo"報告目錄:$REPORT_DIR" echo"========================================" # ============ 測試前檢查 ============ echo"" echo"[1/5] 測試前環境檢查..." # 檢查目標可達 if! curl -sf -o /dev/null -m 5"$TARGET_URL";then echo"ERROR: 無法訪問$TARGET_URL" exit1 fi # 檢查 wrk 工具 if!command-v wrk &> /dev/null;then echo"ERROR: wrk 未安裝" echo"Ubuntu: sudo apt install -y wrk" echo"Rocky: 需要從源碼編譯" exit1 fi # 記錄系統狀態 echo"系統配置:">"$REPORT_DIR/system_info.txt" echo"CPU 核心數:$(nproc)">>"$REPORT_DIR/system_info.txt" echo"內存:$(free -h | grep Mem | awk '{print $2}')">>"$REPORT_DIR/system_info.txt" echo"somaxconn:$(sysctl -n net.core.somaxconn)">>"$REPORT_DIR/system_info.txt" echo"file-max:$(sysctl -n fs.file-max)">>"$REPORT_DIR/system_info.txt" echo"tcp_tw_reuse:$(sysctl -n net.ipv4.tcp_tw_reuse)">>"$REPORT_DIR/system_info.txt" echo"local_port_range:$(sysctl -n net.ipv4.ip_local_port_range)">>"$REPORT_DIR/system_info.txt" # ============ 預熱 ============ echo"" echo"[2/5] 預熱階段(10 秒)..." wrk -t"$THREADS"-c100 -d10s"$TARGET_URL"> /dev/null 2>&1 # ============ 正式測試 ============ echo"" echo"[3/5] 正式測試 ($DURATION)..." wrk -t"$THREADS"-c"$CONNECTIONS"-d"$DURATION"--latency"$TARGET_URL" >"$REPORT_DIR/wrk_output.txt"2>&1 # ============ 采集指標 ============ echo"" echo"[4/5] 采集系統指標..." # 連接狀態統計 ss -s >"$REPORT_DIR/ss_summary.txt"2>&1 # TIME_WAIT 統計 ss -tn state time-wait | wc -l >"$REPORT_DIR/timewait_count.txt"2>&1 # Nginx stub_status(如果可用) curl -sf"http://localhost/nginx_status">"$REPORT_DIR/nginx_status.txt"2>/dev/null ||true # ============ 輸出報告 ============ echo"" echo"[5/5] 測試結果" echo"========================================" cat"$REPORT_DIR/wrk_output.txt" echo"" echo"========================================" echo"TIME_WAIT 連接數:$(cat "$REPORT_DIR/timewait_count.txt")" echo"" if[ -f"$REPORT_DIR/nginx_status.txt"];then echo"Nginx 狀態:" cat"$REPORT_DIR/nginx_status.txt" echo"" fi echo"完整報告保存在:$REPORT_DIR/" echo"========================================"
四、最佳實踐和注意事項
4.1 最佳實踐
4.1.1 不同業務場景的參數模板
場景 A:API 網關(高 QPS,小請求體)
# 特點:請求小、響應小、延遲敏感
upstream api_backend {
server 10.0.1.10:8080;
server 10.0.1.11:8080;
keepalive 256; # 較大的連接池
keepalive_requests 50000;
keepalive_timeout 120s;
}
server {
keepalive_timeout 30; # 較短的客戶端 keepalive
keepalive_requests 500;
location / {
proxy_pass http://api_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_connect_timeout 3s;
proxy_read_timeout 10s;
# 小緩沖區,因為響應通常不大
proxy_buffer_size 4k;
proxy_buffers 8 8k;
}
}
場景 B:文件下載服務(高帶寬,大響應體)
# 特點:大文件、長連接、帶寬密集
upstream file_backend {
server 10.0.1.20:8080;
server 10.0.1.21:8080;
keepalive 32; # 連接數不用太多,但每個連接傳輸時間長
keepalive_requests 100;
keepalive_timeout 300s; # 較長的超時
}
server {
keepalive_timeout 120;
client_max_body_size 10g; # 允許大文件上傳
location /download/ {
proxy_pass http://file_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_read_timeout 600s; # 大文件傳輸需要較長超時
# 大緩沖區 + 臨時文件緩存
proxy_buffering on;
proxy_buffer_size 16k;
proxy_buffers 64 32k;
proxy_busy_buffers_size 128k;
proxy_max_temp_file_size 1024m; # 允許緩存到臨時文件
proxy_temp_path /var/cache/nginx/proxy_temp;
}
}
場景 C:WebSocket 代理(長連接,雙向通信)
# 特點:長連接、雙向數據、連接數多
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream ws_backend {
server 10.0.1.30:8080;
server 10.0.1.31:8080;
# WebSocket 不使用 keepalive 連接池
# 因為 WebSocket 連接是持久化的
}
server {
location /ws/ {
proxy_pass http://ws_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# WebSocket 超時要設長,否則空閑連接會被斷開
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
# 禁用緩沖(WebSocket 需要實時傳輸)
proxy_buffering off;
}
}
4.1.2 灰度調優流程
# 修改 Nginx 配置時的安全流程 # 第一步:在測試環境驗證配置 sudo nginx -t # 第二步:備份當前配置 sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak.$(date +%Y%m%d_%H%M%S) # 第三步:灰度生效 # 如果有多臺 Nginx,先在一臺上 reload sudo nginx -s reload # 第四步:觀察指標(至少 10 分鐘) # 關注:錯誤率、延遲、連接數、CPU/內存 # 第五步:確認無異常后,在其余服務器上逐臺 reload # 第六步:如果發現問題,立即回滾 sudo cp /etc/nginx/nginx.conf.bak.20260313_143000 /etc/nginx/nginx.conf sudo nginx -t && sudo nginx -s reload
4.1.3 安全加固
# 隱藏 Nginx 版本信息 server_tokens off; # 安全響應頭 add_header X-Content-Type-Options nosniff always; add_header X-Frame-Options SAMEORIGIN always; add_header X-XSS-Protection "1; mode=block" always; add_header Referrer-Policy strict-origin-when-cross-origin always; # 限制請求方法 if ($request_method !~ ^(GET|HEAD|POST|PUT|DELETE|PATCH)$) { return 405; } # 禁止訪問隱藏文件 location ~ /. { deny all; access_log off; log_not_found off; }
4.2 注意事項
4.2.1 配置注意事項
worker_connections 與文件描述符的關系:
worker_connections 只是 Nginx 配置層面的限制,實際生效還需要操作系統允許打開那么多文件描述符。必須確保 worker_rlimit_nofile >= worker_connections。推薦設置 worker_rlimit_nofile 為 worker_connections 的 2 倍以上,因為除了網絡連接,Nginx 還需要打開日志文件、緩存文件等。
proxy_buffering 的副作用:
proxy_buffering on(默認)會讓 Nginx 盡快從 upstream 讀取完整響應,釋放 upstream 連接。但如果響應很大且客戶端接收很慢,Nginx 會將響應緩存到內存甚至磁盤臨時文件中。這會增加內存和磁盤 I/O 消耗。對于 Server-Sent Events (SSE) 和流式響應,必須設置 proxy_buffering off。
keepalive_timeout 不宜過長:
客戶端 keepalive 超時設得太長會導致大量空閑連接占用 worker_connections 的配額。如果 10 萬個客戶端都保持 keepalive 但實際只有 1 萬個在發請求,那 9 萬個空閑連接白白占用了資源。一般 30-120 秒是合理范圍。
4.2.2 常見錯誤
| 錯誤現象 | 原因分析 | 解決方案 |
|---|---|---|
| 502 Bad Gateway | upstream 連接失敗或超時 | 檢查 upstream 健康狀態,調整 proxy_connect_timeout |
| 504 Gateway Timeout | upstream 響應超時 | 調整 proxy_read_timeout,排查后端性能 |
| "worker_connections are not enough" | 并發連接超過 worker_connections | 增大 worker_connections,同時調整系統 fd 限制 |
| "cannot assign requested address" | 本地端口耗盡(TIME_WAIT 堆積) | 啟用 upstream keepalive,擴大 ip_local_port_range |
| "too many open files" | 文件描述符限制不足 | 調整 worker_rlimit_nofile 和系統 limits |
| "upstream prematurely closed connection" | upstream 關閉了 keepalive 連接 | 確保 upstream keepalive_timeout > Nginx keepalive_timeout |
| "110: Connection timed out" while connecting to upstream | upstream 的 accept 隊列滿或網絡問題 | 檢查 upstream 的 somaxconn 和 backlog |
| Nginx CPU 100% 某個核 | 單個 worker 負載過重 | 啟用 reuseport 均衡負載 |
4.2.3 兼容性問題
HTTP/2 與 upstream:Nginx 到 upstream 的連接目前不支持 HTTP/2(截至 1.27.x),即使客戶端使用 HTTP/2。upstream 連接始終使用 HTTP/1.1。如果 upstream 是 gRPC 服務,使用 grpc_pass 指令而非 proxy_pass。
HTTP/3 客戶端兼容性:部分老版本瀏覽器和客戶端不支持 HTTP/3。必須同時保留 HTTP/2 監聽以確保兼容性。通過 Alt-Svc 頭通知支持 HTTP/3 的客戶端升級。
TCP Fast Open:需要內核 3.7+ 支持。某些中間網絡設備(防火墻、負載均衡器)可能丟棄 TFO 的 SYN 包。在公網環境建議測試后再啟用。
reuseport 與熱重載:使用 reuseport 時,nginx -s reload 會導致短暫的連接中斷(舊 worker 的 listen socket 被關閉,新 worker 創建新的 listen socket)。如果對零停機有嚴格要求,需要評估影響。
五、故障排查和監控
5.1 故障排查
5.1.1 日志查看
# 查看 Nginx 錯誤日志(實時跟蹤)
sudo tail -f /var/log/nginx/error.log
# 按錯誤級別過濾
grep'[error]'/var/log/nginx/error.log | tail -20
grep'[crit]'/var/log/nginx/error.log | tail -20
grep'[alert]'/var/log/nginx/error.log | tail -20
grep'[emerg]'/var/log/nginx/error.log | tail -20
# 統計錯誤類型分布
awk -F'[][]''/[(error|crit|alert|emerg)]/ {print $2}'
/var/log/nginx/error.log | sort | uniq -c | sort -rn
# 統計 upstream 錯誤
grep"upstream"/var/log/nginx/error.log |
grep -oP'(timed out|connection refused|no live upstreams|prematurely closed)'|
sort | uniq -c | sort -rn
# 分析訪問日志中的 HTTP 狀態碼分布
awk'{print $9}'/var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
# 分析響應時間分布(需要自定義日志格式包含 $request_time)
awk'{print $NF}'/var/log/nginx/access.log |
awk'{
if ($1 < 0.1) bucket="<100ms";
? ? ? ? else if ($1 < 0.5) bucket="100-500ms";
? ? ? ? else if ($1 < 1) bucket="500ms-1s";
? ? ? ? else if ($1 < 5) bucket="1-5s";
? ? ? ? else bucket=">5s";
count[bucket]++
}
END {for (b in count) print count[b], b}'| sort -rn
5.1.2 實時狀態監控
# 使用 stub_status 模塊查看實時連接狀態 curl -s http://localhost/nginx_status # 輸出示例: # Active connections: 12456 # server accepts handled requests # 28473621 28473621 158234512 # Reading: 142 Writing: 1523 Waiting: 10791 # 字段含義: # Active connections -- 當前活躍連接數(包括 Waiting) # accepts -- 總接受連接數 # handled -- 總處理連接數(handled == accepts 表示沒有丟棄連接) # requests -- 總請求數(一個連接可以發多個請求) # Reading -- 正在讀取請求頭的連接數 # Writing -- 正在寫響應的連接數 # Waiting -- keepalive 空閑等待的連接數 # 使用 watch 持續監控 watch -n 1'curl -s http://localhost/nginx_status' # 編寫簡單的監控腳本 whiletrue;do echo"$(date +%H:%M:%S)$(curl -s http://localhost/nginx_status | awk '/Active/{printf "active=%s ",$3} /Reading/{printf "reading=%s writing=%s waiting=%s",$2,$4,$6}')" sleep 5 done
5.1.3 連接狀態分析
# 查看 Nginx 進程的連接數
forpidin$(pgrep -f"nginx: worker");do
fd_count=$(ls /proc/$pid/fd 2>/dev/null | wc -l)
echo"Worker PID$pid:$fd_countfile descriptors"
done
# 查看各狀態的 TCP 連接數
ss -ant | awk'{print $1}'| sort | uniq -c | sort -rn
# 查看到各 upstream 的連接分布
ss -tn | awk'{print $5}'| grep -E"10.0.1.(10|11|12):8080"|
sort | uniq -c | sort -rn
# 查看 SYN 隊列溢出
netstat -s | grep -i"SYNs to LISTEN"
# 或
nstat -a | grep TcpExtListenOverflows
# 查看 accept 隊列溢出
nstat -a | grep TcpExtListenDrops
# 如果這個數字在增長,說明 somaxconn 或 Nginx backlog 不夠大
5.2 性能監控
5.2.1 Prometheus 指標采集
使用 nginx-prometheus-exporter:
# 安裝 nginx-prometheus-exporter # 下載地址:https://github.com/nginxinc/nginx-prometheus-exporter/releases # 運行 exporter(基于 stub_status) nginx-prometheus-exporter -nginx.scrape-uri=http://localhost/nginx_status & # exporter 默認監聽 9113 端口 curl -s http://localhost:9113/metrics | grep nginx # 暴露的關鍵指標: # nginx_connections_active -- 活躍連接數 # nginx_connections_accepted -- 總接受連接數(counter) # nginx_connections_handled -- 總處理連接數(counter) # nginx_connections_reading -- 讀取請求頭的連接數 # nginx_connections_writing -- 發送響應的連接數 # nginx_connections_waiting -- keepalive 空閑連接數 # nginx_http_requests_total -- 總請求數(counter)
Prometheus 采集配置:
# 文件路徑:/etc/prometheus/prometheus.yml(相關片段)
scrape_configs:
-job_name:'nginx'
scrape_interval:15s
static_configs:
-targets:
-'nginx-server-01:9113'
-'nginx-server-02:9113'
# 同時采集 node_exporter 的系統指標
-job_name:'node'
scrape_interval:15s
static_configs:
-targets:
-'nginx-server-01:9100'
-'nginx-server-02:9100'
5.2.2 關鍵監控指標
| 指標名稱 | 正常范圍 | 告警閾值 | 說明 |
|---|---|---|---|
| 活躍連接數 | 取決于業務 | > 80% worker_connections 總量 | 接近上限說明需要擴容 |
| 新建連接速率 | 取決于業務 | 突增 200% 以上 | 可能是流量攻擊或業務突發 |
| HTTP 5xx 錯誤率 | < 0.1% | > 1% | 502/504 是最常見的上游問題 |
| 請求延遲 P99 | < 500ms | > 2s | 尾部延遲反映最差情況 |
| Worker CPU 使用率 | < 80% | > 90% | 單個 worker 過高說明負載不均 |
| TIME_WAIT 連接數 | < 5000 | > 20000 | 持續增長說明 keepalive 配置有問題 |
| accept 隊列溢出 | 0 | > 0 且持續增長 | 需要增大 somaxconn 或 backlog |
| dropped connections | 0 | > 0 | handled != accepted 說明有連接被丟棄 |
5.2.3 告警規則
# 文件路徑:/etc/prometheus/rules/nginx_alerts.yml
groups:
-name:nginx_alerts
rules:
# 連接數接近上限
-alert:NginxConnectionsHigh
expr:nginx_connections_active>50000
for:5m
labels:
severity:warning
annotations:
summary:"Nginx 活躍連接數過高"
description:"{{ $labels.instance }}活躍連接{{ $value }},接近配置上限"
# 5xx 錯誤率過高
-alert:NginxHighErrorRate
expr:|
rate(nginx_http_requests_total{status=~"5.."}[5m])
/
rate(nginx_http_requests_total[5m])
> 0.01
for:5m
labels:
severity:critical
annotations:
summary:"Nginx 5xx 錯誤率過高"
description:"{{ $labels.instance }}5xx 錯誤率{{ $value | humanizePercentage }}"
# 連接被丟棄
-alert:NginxConnectionsDropped
expr:|
(rate(nginx_connections_accepted[5m]) - rate(nginx_connections_handled[5m])) > 0
for:5m
labels:
severity:critical
annotations:
summary:"Nginx 存在丟棄的連接"
description:"{{ $labels.instance }}正在丟棄連接,每秒約{{ $value }}個"
# Nginx 進程消失
-alert:NginxDown
expr:nginx_up==0
for:1m
labels:
severity:critical
annotations:
summary:"Nginx 服務不可用"
description:"{{ $labels.instance }}Nginx 進程未運行"
5.3 備份與恢復
5.3.1 配置備份
#!/bin/bash
# 文件名:nginx_config_backup.sh
# 功能:備份 Nginx 完整配置
BACKUP_DIR="/backup/nginx/$(date +%Y%m%d_%H%M%S)"
mkdir -p"$BACKUP_DIR"
# 備份配置文件
cp -r /etc/nginx/"$BACKUP_DIR/nginx-config/"
# 備份 SSL 證書
cp -r /etc/nginx/ssl/"$BACKUP_DIR/ssl/"2>/dev/null
# 備份 systemd 覆蓋配置
cp -r /etc/systemd/system/nginx.service.d/"$BACKUP_DIR/systemd/"2>/dev/null
# 備份系統調優參數
cp /etc/sysctl.d/99-nginx-*.conf"$BACKUP_DIR/"2>/dev/null
cp /etc/security/limits.d/99-nginx.conf"$BACKUP_DIR/"2>/dev/null
# 記錄當前狀態
nginx -V >"$BACKUP_DIR/nginx_version.txt"2>&1
nginx -T >"$BACKUP_DIR/nginx_full_config.txt"2>&1
# 壓縮
tar czf"${BACKUP_DIR}.tar.gz"-C"$(dirname $BACKUP_DIR)""$(basename $BACKUP_DIR)"
rm -rf"$BACKUP_DIR"
echo"Nginx 配置已備份到${BACKUP_DIR}.tar.gz"
5.3.2 恢復流程
# 1. 解壓備份 tar xzf /backup/nginx/20260313_143000.tar.gz -C /tmp/ # 2. 恢復配置 sudo cp -r /tmp/20260313_143000/nginx-config/* /etc/nginx/ # 3. 檢查配置語法 sudo nginx -t # 4. 重載配置 sudo nginx -s reload # 5. 驗證 curl -I http://localhost/
六、總結
6.1 技術要點回顧
Nginx 高并發能力的基礎是 epoll 事件驅動模型,單個 worker 進程可以處理數萬并發連接,調優時不要盲目增加 worker 數量
系統層調優(文件描述符、somaxconn、端口范圍、BBR)是基礎,Nginx 配置層面的參數再大也無法突破系統限制
upstream keepalive 是高并發反向代理中效果最顯著的優化項,必須配合 proxy_http_version 1.1 和 proxy_set_header Connection "" 使用
TIME_WAIT 堆積的根因通常是 upstream 連接未復用,啟用 keepalive 后配合 tcp_tw_reuse 可以有效解決
reuseport 消除了 accept_mutex 的鎖競爭,在高新建連接速率場景下提升顯著
限流(limit_conn / limit_req)是保護 Nginx 和后端服務的最后一道防線,必須在上線前配置好
不同業務場景(API 網關、文件下載、WebSocket)的調優參數差異很大,不存在萬能配置
6.2 進階學習方向
Nginx 內部機制深入:連接池管理、事件調度器實現、內存池分配策略、變量延遲求值機制。通過閱讀 Nginx 源碼理解 ngx_event_module 和 ngx_http_module 的交互。
OpenResty / Lua 擴展:在 Nginx 中嵌入 Lua 腳本實現動態路由、復雜限流、A/B 測試、灰度發布。OpenResty 的 cosocket API 支持在 Nginx 中進行非阻塞網絡操作。
QUIC/HTTP3 深度調優:QUIC 協議的擁塞控制、0-RTT 連接恢復、連接遷移等特性的調優和生產實踐。
6.3 參考資料
Nginx 官方文檔: https://nginx.org/en/docs/
Nginx 管理指南: https://docs.nginx.com/nginx/admin-guide/
Linux 網絡棧調優: https://www.kernel.org/doc/html/latest/networking/
wrk HTTP 壓測工具: https://github.com/wg/wrk
nginx-prometheus-exporter: https://github.com/nginxinc/nginx-prometheus-exporter
BBR 擁塞控制: https://github.com/google/bbr
man 7 tcp / man 7 socket / man 2 epoll_wait
附錄
A. 命令速查表
# Nginx 管理 nginx -t # 檢查配置語法 nginx -T # 輸出完整配置 nginx -s reload # 重載配置 nginx -s stop # 停止服務 nginx -V # 查看編譯參數 # 系統參數查看 sysctl net.core.somaxconn # TCP accept 隊列 sysctl net.ipv4.ip_local_port_range # 本地端口范圍 sysctl net.ipv4.tcp_tw_reuse # TIME_WAIT 復用 sysctl fs.file-max # 系統文件描述符上限 ulimit-n # 當前用戶文件描述符限制 # 連接狀態 ss -s # TCP 連接狀態匯總 ss -tn state time-wait | wc -l # TIME_WAIT 數量 ss -tn state established | wc -l # ESTABLISHED 數量 nstat -a | grep TcpExtListenOverflows # accept 隊列溢出 # Nginx 狀態 curl -s http://localhost/nginx_status # stub_status curl -s http://localhost:9113/metrics # Prometheus 指標 # 壓測 wrk -t8 -c10000 -d60s http://target/ # wrk 壓測
B. 配置參數詳解
nginx.conf 核心參數說明:
worker_processes:worker 進程數,auto 根據 CPU 核心數設置
worker_cpu_affinity:CPU 親和性綁定,auto 自動分配
worker_rlimit_nofile:每個 worker 的文件描述符限制
worker_connections:每個 worker 的最大連接數
multi_accept:是否一次 accept 多個連接
accept_mutex:accept 互斥鎖,使用 reuseport 時建議關閉
keepalive_timeout:客戶端 keepalive 空閑超時
keepalive_requests:單個 keepalive 連接的最大請求數
reset_timedout_connection:超時連接發 RST 而非 FIN
proxy_http_version:到 upstream 的 HTTP 版本,keepalive 需要 1.1
upstream keepalive:upstream 空閑連接池大?。?worker)
upstream keepalive_requests:upstream keepalive 連接最大請求數
upstream keepalive_timeout:upstream keepalive 空閑超時
proxy_buffer_size:響應頭緩沖區大小
proxy_buffers:響應體緩沖區(塊數和每塊大?。?/p>
limit_conn:并發連接數限制
limit_req:請求速率限制(rate + burst + nodelay)
reuseport:listen 指令的參數,每個 worker 獨立 listen socket
C. 術語表
| 術語 | 英文 | 解釋 |
|---|---|---|
| epoll | Event Poll | Linux 高效 I/O 事件通知機制,支持 O(1) 事件獲取 |
| 驚群效應 | Thundering Herd | 多個進程/線程同時被喚醒競爭同一個事件,大部分白白喚醒 |
| reuseport | SO_REUSEPORT | 允許多個 socket 綁定同一端口,內核層負載均衡 |
| backlog | Listen Backlog | TCP accept 隊列長度,等待被 accept() 的已完成連接 |
| TIME_WAIT | TIME_WAIT State | TCP 連接關閉后的等待狀態,默認持續 60 秒(2MSL) |
| keepalive | HTTP Keep-Alive | TCP 連接復用,一個連接承載多個 HTTP 請求 |
| upstream | 上游服務器 | Nginx 反向代理后面的應用服務器 |
| worker_connections | Worker 連接數 | 每個 worker 進程能同時處理的最大連接數 |
| BBR | Bottleneck Bandwidth and RTT | Google 開發的 TCP 擁塞控制算法 |
| TFO | TCP Fast Open | TCP 快速打開,減少握手延遲 |
| QUIC | Quick UDP Internet Connections | Google 開發的傳輸層協議,HTTP/3 的基礎 |
| stub_status | Nginx 狀態模塊 | 暴露連接數、請求數等基礎指標的內置模塊 |
| proxy_buffering | 代理緩沖 | Nginx 緩存 upstream 響應,解耦上下游速度差異 |
| burst | 突發容量 | limit_req 中允許的突發請求數,超過 rate 但在 burst 內的請求進入隊列 |
-
Linux
+關注
關注
88文章
11784瀏覽量
219284 -
操作系統
+關注
關注
37文章
7417瀏覽量
129468 -
nginx
+關注
關注
0文章
189瀏覽量
13158
原文標題:Nginx 高并發連接調優實戰手冊
文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
Nginx高并發連接調優實戰手冊
評論