伺服器維運

Nginx 是什麼意思:能做什麼、和 Apache 怎麼選

Nginx(讀作 engine x)是一款開源的 Web 伺服器,同時承擔反向代理、負載均衡、HTTPS 終結和快取。它通常放在網站最前面:靜態檔案自己返回,動態請求轉給後端程式。事件驅動的架構讓它用很少的程序維持大量連線,高併發下比傳統的 Apache 省記憶體。

作者 頂聯產品團隊發布於 6 分鐘閱讀

Nginx 是什麼意思?Nginx 讀作“engine x”,是一款開源的 Web 伺服器,同時也是反向代理、負載均衡器和 HTTP 快取。它由 Igor Sysoev 開發,2004 年公開發布,最初就是為了解決單台伺服器同時處理上萬個連線的問題。今天它最常見的位置是網站的最前面:圖片、CSS、JS 這些靜態檔案由它直接返回,PHP、Java、Node.js、Python 寫的動態請求由它轉給後端程式,HTTPS 證書也在它這裡處理。與 Apache 相比,它用事件驅動模型處理連線,少數幾個程序就能維持成千上萬個連線,所以高併發場景下更省記憶體。

Nginx 能做什麼:五種常見用法

用法 做什麼 關鍵配置指令
靜態網站與檔案服務 直接返回 HTML、圖片、CSS、JS 和下載檔案 root、index、try_files、expires
反向代理 接收客戶端請求,轉給後端程式,客戶端看不到後端位址 proxy_pass、proxy_set_header
負載均衡 把請求分給多台後端,連續失敗的後端暫時摘除 upstream、least_conn、max_fails
HTTPS 終結 在 Nginx 上完成 TLS 握手,後端只跑普通 HTTP ssl_certificate、ssl_protocols
快取 把後端的回應存到本地,重複請求不再打到後端 proxy_cache_path、proxy_cache

此外還能做 TCP/UDP 四層轉發(stream 模組,例如轉發 MySQL 的 3306 連接埠)、gzip 壓縮和按 IP 限流。

下面一份配置把五種用法放在一起:一個商城站點,靜態檔案由 Nginx 返回,介面轉給 3 台應用伺服器,商品列表介面快取 10 分鐘。放在 /etc/nginx/conf.d/shop.conf,這個目錄裡的檔案會被包含進主配置的 http 塊:

# 快取目錄:1 MB 的 keys_zone 大約能存 8000 個快取鍵
proxy_cache_path /var/cache/nginx/shop levels=1:2 keys_zone=shop_cache:50m
                 max_size=2g inactive=60m use_temp_path=off;

# 負載均衡:請求優先給當前連線數最少的後端,30 秒內失敗 3 次就暫停使用 30 秒
upstream shop_app {
    least_conn;
    server 10.0.0.11:8080 max_fails=3 fail_timeout=30s;
    server 10.0.0.12:8080 max_fails=3 fail_timeout=30s;
    server 10.0.0.13:8080 backup;    # 前兩台都不可用時才啟用
}

# HTTP 統一跳轉到 HTTPS
server {
    listen 80;
    server_name shop.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    http2 on;                        # 1.25.1 起的寫法,舊版本改為 listen 443 ssl http2;
    server_name shop.example.com;

    ssl_certificate     /etc/nginx/ssl/shop.example.com.fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/shop.example.com.key;
    ssl_protocols       TLSv1.2 TLSv1.3;

    # 靜態檔案:由 Nginx 直接讀磁碟返回,瀏覽器快取 7 天
    location /static/ {
        root /var/www/shop;          # 實際讀取 /var/www/shop/static/...
        expires 7d;
    }

    # 商品列表介面:快取 10 分鐘,後端出錯時先返回舊快取
    location /api/products/ {
        proxy_cache shop_cache;
        proxy_cache_valid 200 10m;
        proxy_cache_use_stale error timeout updating http_500 http_502 http_503;
        add_header X-Cache-Status $upstream_cache_status;
        proxy_pass http://shop_app;
        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;
    }

    # 其餘請求:反向代理到應用伺服器
    location / {
        proxy_pass http://shop_app;
        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;
    }
}

幾個要點:

  • 四個 proxy_set_header 不能省。不傳 Host,後端拿到的是 upstream 名稱而不是域名;不傳 X-Forwarded-For 和 X-Real-IP,後端日誌裡所有訪客的 IP 都是 Nginx 自己;不傳 X-Forwarded-Proto,後端以為使用者走的是 HTTP,可能反覆跳轉到 HTTPS。
  • 開源版只有被動健康檢查。max_fails 和 fail_timeout 靠真實請求失敗來判斷後端是否可用;定時主動探測後端屬於商業版 NGINX Plus 的功能,開源版需要第三方模組或在後端前面另加檢測。
  • 驗證快取是否生效。連續請求兩次 curl -sI https://shop.example.com/api/products/ | grep -i x-cache-status,第一次是 MISS,第二次是 HIT(走 HTTP/2 時回應頭名稱是小寫,所以 grep 要加 -i)。如果一直是 MISS,先看後端回應是否帶了 Set-Cookie 或禁止快取的 Cache-Control,預設情況下 Nginx 不快取這類回應。
  • PHP 程式走 FastCGI。Nginx 不執行 PHP,要把 .php 請求交給 PHP-FPM:在 location ~ \.php$ 裡寫 include fastcgi.conf; 和 fastcgi_pass unix:/run/php/php8.3-fpm.sock;。注意 fastcgi.conf 比 fastcgi_params 多一行 SCRIPT_FILENAME,只 include 後者又沒補這一行,PHP-FPM 會找不到指令碼;Debian、Ubuntu 上也可以直接用 include snippets/fastcgi-php.conf;。socket 路徑以伺服器上實際安裝的 PHP 版本為準。

為什麼 Nginx 在高併發下更省資源

Nginx 啟動後是一個 master 程序加若干 worker 程序。master 只負責讀配置、管理 worker;真正處理請求的是 worker,數量通常設為 worker_processes auto;,即與 CPU 核數相同。每個 worker 是單執行緒的事件迴圈:它把幾千個連線都交給核心的 epoll(BSD 上是 kqueue)監視,哪個連線有資料可讀、可寫,核心就通知它去處理哪個,處理完立刻轉向下一個,從不停下來等某一個慢客戶端。

傳統的 Apache prefork 模式則是一個連線佔一個程序。使用者的瀏覽器開著 keep-alive 什麼也不發,這個程序也只能乾等。兩種模型面對 1 萬個併發連線時的差別:

對比 Nginx Apache prefork
處理 1 萬個連線需要 4 核伺服器上 4 個 worker,每個維持約 2500 個連線(worker_connections 要相應調高) 理論上 1 萬個程序;預設 MaxRequestWorkers 只有 256,超出的連線排隊等待
空閒 keep-alive 連線的開銷 很小:Nginx 官網的說法是 1 萬個空閒 keep-alive 連線約佔 2.5 MB 記憶體 每個連線佔一個完整程序,載入了 mod_php 時每個程序常有幾十 MB
慢客戶端的影響 只佔一個連線的少量記憶體 長時間佔住一個程序

Nginx 能維持的連線上限大約是 worker_processes × worker_connections。發行版自帶的配置裡 worker_connections 常見 768 或 1024,做反向代理時每個請求要佔用客戶端和後端兩個連線,實際能服務的併發約為上限的一半;要調高時,同時用 worker_rlimit_nofile 提高每個程序能開啟的檔案數。

事件驅動也有代價:如果某個操作阻塞了 worker(例如在很慢的磁碟上讀大檔案,或者第三方模組裡有同步呼叫),這個 worker 上的所有連線都會跟著卡住。Nginx 為此提供了執行緒池(aio threads),把讀磁碟這類阻塞操作交給後台執行緒。另外,Apache 2.4 的 event 模式也把空閒的 keep-alive 連線交給專門的監聽執行緒管理,差距已經比 prefork 時代小,但每個正在處理的請求仍然要佔用一個執行緒。

在伺服器上可以直接看到這兩種結構:

ps -o pid,ppid,user,rss,cmd -C nginx     # 1 個 master(root)加若干 worker,RSS 單位是 KB
grep -E 'worker_processes|worker_connections' /etc/nginx/nginx.conf
nproc                                    # CPU 核數,worker_processes auto 時與它相同
apachectl -V | grep -i mpm               # Apache 當前用的 MPM:prefork、worker 或 event

Nginx 和 Apache 的區別

對比項 Nginx Apache httpd
連線處理模型 事件驅動,少數 worker 程序各自處理大量連線 由 MPM 決定:prefork 一連線一程序,worker 和 event 為多程序多執行緒
靜態檔案、高併發 記憶體佔用低,適合做前端入口 並發上去後程序或執行緒數隨之增長
執行 PHP 交給 PHP-FPM 可以用 mod_php 內嵌執行,也可以接 PHP-FPM
目錄級配置 不支援 .htaccess,規則寫在主配置裡,改完要 reload 支援 .htaccess,網站目錄裡放檔案即可生效
偽靜態規則 rewrite、try_files mod_rewrite,很多 PHP 程式自帶 .htaccess 規則
模組 多數編譯程序序;動態模組要和 Nginx 版本匹配 模組動態載入,Debian、Ubuntu 上用 a2enmod 一條命令啟用
反向代理與負載均衡 核心功能,配置簡潔 用 mod_proxy、mod_proxy_balancer 實現
許可證 BSD 類許可證 Apache License 2.0
典型位置 網站入口、反向代理、靜態資源、負載均衡 共享虛擬主機、依賴 .htaccess 的老程式

兩者的分界其實就在兩點:要不要 .htaccess、併發有多高。共享虛擬主機讓每個使用者在自己目錄裡改規則,離不開 .htaccess,所以多用 Apache 或相容它的 LiteSpeed;自己有整台伺服器、能改主配置的,用 Nginx 的限制就不存在了。

還有一種常見的混合方案:Nginx 在前面處理 HTTPS、靜態檔案和連線,Apache 在後面跑依賴 .htaccess 的老程式。這時 Apache 看到的來源 IP 全是 Nginx 的位址,要啟用 mod_remoteip 並信任 Nginx 傳來的 X-Forwarded-For,日誌和程式裡才能拿到訪客的真實 IP。

租伺服器建站,什麼情況選 Nginx

場景 選擇 理由
1–2 GB 記憶體的雲伺服器或 VPS 跑 WordPress 等 PHP 網站 Nginx 加 PHP-FPM 靜態檔案和空閒連線不佔 PHP 程序,記憶體更夠用
Node.js、Java、Python、Go 寫的應用 Nginx 做反向代理 應用只監聽 127.0.0.1,由 Nginx 對外處理 80、443 和證書
多台後端伺服器 Nginx 的 upstream 負載均衡和故障摘除開箱即用
圖片站、下載站、純靜態網站 Nginx 靜態檔案吞吐高,配置簡單
程式大量依賴 .htaccess,且不想改寫規則 Apache,或前 Nginx 後 Apache 免去把規則逐條翻譯成 Nginx 寫法
用寶塔等面板建站 兩者都可以 面板裡切換,按上面的規則選

小記憶體伺服器上,Nginx 的優勢可以算出來。假設一台 2 GB 記憶體的伺服器,系統和 MySQL 佔去約 1 GB,剩下約 1000 MB 給 PHP,用下面的命令實測每個 PHP 程序平均佔 40 MB:

ps -o rss= -C php-fpm8.3 | awk '{s+=$1; n++} END {printf "%d 個程序,平均 %.1f MB\n", n, s/n/1024}'

那麼 PHP 最多開 1000 ÷ 40 = 25 個程序,PHP-FPM 的 pm.max_children 就設為 25。區別在於這 25 個程序被誰佔用:用 Apache prefork 加 mod_php 時,每個連線都佔一個帶 PHP 的程序,包括下載一張圖片、甚至什麼都不發的 keep-alive 連線,一個頁面帶 30 個靜態資源,幾個訪客同時開啟就可能把 25 個程序佔滿,後來的人只能排隊;用 Nginx 時,靜態檔案和空閒連線由 Nginx 處理,25 個 PHP-FPM 程序只服務真正需要執行 PHP 的請求。

裝好 Nginx 後,記得放行 80 和 443 連接埠,Ubuntu 上用 ufw:

sudo ufw allow 22/tcp          # 先放行 SSH,否則啟用防火牆後可能再也連不上
sudo ufw allow 80,443/tcp
sudo ufw enable
sudo ufw status

第一行最容易忘:啟用 ufw 前沒有放行 22 連接埠,當前會話可能還連著,退出後就再也登入不上 SSH。真遇到這種情況,用服務商控制面板裡的 VNC 控制檯登入修改,例如基於 Toplink DCIM 的 客戶自助端 就能直接在瀏覽器裡開啟伺服器控制檯,不依賴 SSH。雲伺服器還要在安全組裡放行同樣的連接埠。

常用命令與配置檔案位置

專案 Debian、Ubuntu 軟體源 RHEL、Rocky、AlmaLinux 及 nginx.org 官方包
安裝 apt install nginx dnf install nginx
主配置 /etc/nginx/nginx.conf /etc/nginx/nginx.conf
站點配置 /etc/nginx/sites-available/,軟連結到 sites-enabled/ 生效;也讀取 conf.d/ /etc/nginx/conf.d/*.conf
預設網站目錄 /var/www/html /usr/share/nginx/html
日誌 /var/log/nginx/access.log、error.log 同左
worker 執行使用者 www-data nginx
nginx -v                         # 版本號:偶數小版本(如 1.26)是 stable,奇數是 mainline
nginx -V 2>&1 | tr ' ' '\n' | grep -E '^--with'   # 編譯了哪些模組
sudo nginx -t                    # 檢查配置語法,改完配置先執行它
sudo nginx -T | less             # 列印合並後的完整配置,排查“改了不生效”
sudo systemctl reload nginx      # 平滑載入新配置
sudo tail -f /var/log/nginx/error.log

改配置的固定順序是:編輯 → nginx -t → systemctl reload nginx → 看 error.log。改配置不需要 restart:reload 時 master 程序用新配置啟動新的 worker,舊 worker 處理完手頭的請求再退出,訪客不會斷開;新配置有錯時 reload 不會生效,線上仍按舊配置執行。跳過檢查直接 restart,配置一錯 Nginx 就起不來了。

常見問題

Nginx 和 Tomcat 有什麼區別?

Tomcat 是執行 Java Web 應用的容器,負責執行 Servlet 和 JSP;Nginx 不執行 Java 程式碼。常見搭配是 Nginx 在前面處理 HTTPS、靜態檔案和負載均衡,把動態請求用 proxy_pass 轉給後面的一台或多台 Tomcat。

Nginx 免費嗎?可以商用嗎?

開源版 Nginx 以 BSD 類許可證釋出,免費使用,包括商業用途。F5 另外提供商業版 NGINX Plus,增加主動健康檢查、動態配置介面和官方支援。國內常見的 OpenResty、Tengine 是基於 Nginx 的衍生版本。

Nginx 報 502 和 504 是什麼原因?

502 Bad Gateway 表示 Nginx 連不上後端,或後端返回了無效回應,常見於後端程式沒啟動、proxy_pass 或 fastcgi_pass 的位址寫錯、PHP-FPM 程序崩潰;504 Gateway Timeout 表示後端在超時時間內沒有回應,proxy_read_timeout 預設 60 秒。先看 error.log 裡帶 upstream 字樣的報錯,再去查後端。

想看看在你的機房裡怎麼用?

從一次產品示範開始,梳理你的業務鏈路。

查看價格
服務熱線 400-112-2951

讓我們聊聊你的 IDC 業務

掃碼新增企業微信,預約產品示範或申請試用。

Toplink 企業微信二維碼

長按儲存或使用企業微信 / 微信掃描

也可致電
400-112-2951
Telegram
@TopLink88