Nginx 反向代理與負(fù)載均衡:一臺服務(wù)器扛不住的解決方案
問題引入:半夜被報(bào)警短信炸醒的滋味
上個(gè)月有個(gè)周三,凌晨兩點(diǎn),我被釘釘報(bào)警震醒了。
打開手機(jī)一看,全是 “Tomcat 響應(yīng)超時(shí)”、“接口 504 Gateway Timeout” 的告警。公司業(yè)務(wù)最近推廣了一波,日活從幾千蹭蹭漲到了幾萬,結(jié)果那臺孤零零的 Tomcat 服務(wù)器開始扛不住了。
白天用戶訪問慢還能忍,到了高峰期直接頻繁 502/504,客服群里炸鍋,老板在群里 @ 我:“怎么回事?修一下。”
我爬起來看監(jiān)控,CPU 飆到 90%,內(nèi)存快滿了,GC 頻率高得嚇人。擴(kuò)容機(jī)器?可以,但一臺 8 核 16G 的服務(wù)器也不便宜啊。而且就算硬件升級了,單點(diǎn)故障的問題還是沒解決——這臺 Tomcat 一掛,整個(gè)服務(wù)就全涼了。
說白了,一臺服務(wù)器就像一家只有一個(gè)廚師的餐廳,飯點(diǎn)一到,客人全堵在柜臺前,廚師累得手忙腳亂,后面的客人等不及就開始罵娘。
那怎么辦?多請幾個(gè)廚師,再找個(gè)機(jī)靈的服務(wù)員在門口統(tǒng)籌安排——客人來了,服務(wù)員負(fù)責(zé)引導(dǎo)到不同的廚師那兒,誰閑了給誰派活。這樣既能分流壓力,某個(gè)廚師請假了,其他廚師還能繼續(xù)干活。
這個(gè)"機(jī)靈的服務(wù)員",就是咱們今天要聊的 Nginx。
方案分析:為什么選 Nginx?
我一開始也想過幾個(gè)方案:
方案 A:直接升級 Tomcat 服務(wù)器配置
- 優(yōu)點(diǎn):簡單,改個(gè)云服務(wù)器配置就行
- 缺點(diǎn):貴,而且單點(diǎn)故障沒解決,Tomcat 本身并發(fā)處理能力也有限
方案 B:用 Tomcat 集群 + 硬件負(fù)載均衡
- 優(yōu)點(diǎn):穩(wěn)定
- 缺點(diǎn):硬件 F5 貴得離譜,小公司玩不起
方案 C:Nginx 反向代理 + 多 Tomcat 節(jié)點(diǎn)
- 優(yōu)點(diǎn):免費(fèi)開源、性能彪悍、配置靈活、社區(qū)生態(tài)成熟
- 缺點(diǎn):需要學(xué)一點(diǎn)配置語法(但其實(shí)不難)
對比下來,Nginx 就是性價(jià)比之王。它不僅能做反向代理和負(fù)載均衡,還能處理靜態(tài)資源、做緩存、壓縮、HTTPS 終止、限流防刷……簡直就是運(yùn)維界的瑞士軍刀。
但這里有個(gè)概念很多新手容易懵:反向代理和正向代理到底啥區(qū)別?
用餐廳服務(wù)員來類比
正向代理,就像你(客戶端)想打電話給某個(gè)明星,但你不方便直接打,于是找了個(gè)中間人(代理)幫你打。明星不知道電話是你打的,只知道是中間人打的。正向代理代理的是客戶端,隱藏的是"你"。
反向代理,就像你去一家高檔餐廳吃飯,門口有個(gè)服務(wù)員接待你,把你領(lǐng)到具體的廚師那兒。你根本不知道廚師是誰、在哪,你只跟服務(wù)員打交道。反向代理代理的是服務(wù)端,隱藏的是"后廚"。
Nginx 就是那個(gè)站在門口的服務(wù)員。用戶訪問的是 Nginx 的 80 端口,Nginx 再把請求轉(zhuǎn)發(fā)給后端的 Tomcat 1、Tomcat 2、Tomcat 3……用戶完全感知不到后端有幾臺服務(wù)器。
實(shí)現(xiàn)過程:Step by Step 上手
好了,概念聊完了,咱們來點(diǎn)實(shí)在的。假設(shè)你現(xiàn)在有兩臺 Tomcat,分別跑在 192.168.1.101:8080 和 192.168.1.102:8080,咱們用 Nginx 把它們代理起來。
Step 1:安裝 Nginx(略過編譯安裝的坑)
如果你用 CentOS,直接 yum 裝:
# 添加 EPEL 源后安裝 sudo yum install nginx -y # 啟動(dòng)并設(shè)置開機(jī)自啟 sudo systemctl start nginx sudo systemctl enable nginx
Ubuntu 的話用 apt install nginx。編譯安裝太折騰,新手建議先用包管理器,把核心概念搞明白再說。
Step 2:配置反向代理
Nginx 的核心配置文件一般在 /etc/nginx/nginx.conf,但咱們更常在 /etc/nginx/conf.d/ 下創(chuàng)建獨(dú)立的 .conf 文件,方便管理。
先來看一個(gè)最簡版的反向代理配置:
# /etc/nginx/conf.d/myapp.conf
server {
listen 80;
server_name api.example.com;
location / {
# 把所有請求轉(zhuǎn)發(fā)到后端 Tomcat
proxy_pass http://192.168.1.101:8080;
# 關(guān)鍵:把客戶端真實(shí) IP 傳給后端,不然 Tomcat 日志里全是 Nginx 的 IP
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}這段要干嘛? 告訴 Nginx:所有訪問 api.example.com:80 的請求,都幫我甩給 192.168.1.101:8080。
關(guān)鍵點(diǎn)在哪?
proxy_pass是核心,指定后端地址proxy_set_header這三行非常重要,否則后端拿不到用戶的真實(shí) IP 和原始 Host,有些業(yè)務(wù)邏輯會(huì)出問題
配置寫完后,一定要執(zhí)行 reload:
sudo nginx -s reload
很多新手改完配置發(fā)現(xiàn)沒生效,就是因?yàn)橥诉@步。后面我會(huì)專門講這個(gè)坑。
Step 3:負(fù)載均衡——一臺不夠,多臺一起扛
現(xiàn)在咱們有兩臺 Tomcat 了,總不能只代理一臺吧?Nginx 的 upstream 模塊就是干這個(gè)的。
# 先定義一個(gè)后端服務(wù)器池,叫 tomcat_cluster
upstream tomcat_cluster {
server 192.168.1.101:8080;
server 192.168.1.102:8080;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://tomcat_cluster; # 注意這里寫的是 upstream 的名字
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}這段要干嘛? 把請求輪流分發(fā)給兩臺 Tomcat,實(shí)現(xiàn)最基本的負(fù)載均衡。
默認(rèn)策略是輪詢(round-robin),就是請求 1 給 101,請求 2 給 102,請求 3 給 101,依次循環(huán)。
Step 4:四種負(fù)載均衡策略,怎么選?
Nginx 支持好幾種負(fù)載均衡策略,不同的場景用不同的策略,別只會(huì)輪詢。
1. 輪詢(round-robin)——默認(rèn)策略
upstream tomcat_cluster {
server 192.168.1.101:8080;
server 192.168.1.102:8080;
}適用場景:后端機(jī)器配置差不多,請求處理時(shí)間也差不多。簡單公平。
2. 權(quán)重(weight)——能者多勞
upstream tomcat_cluster {
server 192.168.1.101:8080 weight=3;
server 192.168.1.102:8080 weight=1;
}適用場景:兩臺機(jī)器配置不一樣,101 是 8 核 16G,102 是 4 核 8G。那就讓 101 多扛點(diǎn)活,權(quán)重設(shè)為 3:1。
3. ip_hash——同一個(gè)用戶始終落在同一臺機(jī)器
upstream tomcat_cluster {
ip_hash;
server 192.168.1.101:8080;
server 192.168.1.102:8080;
}適用場景:你的應(yīng)用用了本地 Session 存儲,用戶登錄狀態(tài)存在 Tomcat 內(nèi)存里。如果請求被分配到不同機(jī)器,用戶就會(huì)頻繁掉線。
但要注意:ip_hash 不是萬能的,如果某臺機(jī)器掛了,原本落在這臺的請求會(huì)重新 hash 到其他機(jī)器。而且如果用戶在公司內(nèi)網(wǎng),出口 IP 相同,可能會(huì)導(dǎo)致某一臺機(jī)器壓力特別大。
4. least_conn——誰閑給誰
upstream tomcat_cluster {
least_conn;
server 192.168.1.101:8080;
server 192.168.1.102:8080;
}適用場景:接口處理時(shí)間差異很大,有的請求 10ms 搞定,有的要 10 秒。least_conn 會(huì)把新請求發(fā)給當(dāng)前連接數(shù)最少的機(jī)器,更智能一些。
我的建議:如果做了分布式 Session(比如 Redis 存 Session),優(yōu)先用 輪詢 或 least_conn;如果還是本地 Session,臨時(shí)用 ip_hash 過渡,但長遠(yuǎn)看還是要上分布式 Session。
Step 5:靜態(tài)資源分離——別讓 Tomcat 干雜活
Tomcat 處理動(dòng)態(tài)請求還行,但讓它去傳圖片、CSS、JS,那就是大材小用,還拖累動(dòng)態(tài)接口的響應(yīng)速度。
Nginx 傳靜態(tài)文件的能力是 Tomcat 的幾十倍,所以咱們要讓 Nginx 直接處理靜態(tài)資源,動(dòng)態(tài)請求才轉(zhuǎn)發(fā)給 Tomcat。
server {
listen 80;
server_name api.example.com;
root /usr/share/nginx/html;
# 靜態(tài)資源直接由 Nginx 返回
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2)$ {
expires 30d; # 緩存 30 天
add_header Cache-Control "public, immutable";
access_log off; # 關(guān)閉靜態(tài)資源訪問日志,減少磁盤 IO
}
# 動(dòng)態(tài)請求轉(zhuǎn)發(fā)給 Tomcat
location /api/ {
proxy_pass http://tomcat_cluster;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}這段要干嘛? 圖片、CSS、JS 這些文件,Nginx 自己從磁盤讀出來返回給用戶;只有 /api/ 開頭的接口請求,才轉(zhuǎn)發(fā)給 Tomcat。
效果很明顯:我上次加了這個(gè)配置后,Tomcat 的 CPU 使用率直接降了 30%,頁面加載速度也快了一截。
Step 6:Gzip 壓縮——讓傳輸更快
現(xiàn)在的前端資源動(dòng)不動(dòng)就幾百 KB,開啟 Gzip 壓縮能省不少帶寬。
http {
# 在 nginx.conf 的 http 塊里加
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
gzip_min_length 1k; # 小于 1K 的文件不壓縮,省 CPU
gzip_comp_level 4; # 壓縮級別 1-9,4 是性價(jià)比平衡點(diǎn)
}為什么要設(shè) gzip_min_length 1k? 因?yàn)閴嚎s本身也要消耗 CPU,如果文件本來就幾字節(jié),壓縮后可能反而更大,得不償失。
Step 7:HTTPS 配置——現(xiàn)在沒 HTTPS 都不好意思上線
申請一個(gè)免費(fèi) SSL 證書(Let’s Encrypt 或者阿里云免費(fèi)證書),配置很簡單:
server {
listen 443 ssl;
server_name api.example.com;
ssl_certificate /etc/nginx/ssl/api.example.com.crt;
ssl_certificate_key /etc/nginx/ssl/api.example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
proxy_pass http://tomcat_cluster;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
# HTTP 自動(dòng)跳 HTTPS
server {
listen 80;
server_name api.example.com;
return 301 https://$server_name$request_uri;
}關(guān)鍵提醒:HTTPS 配完后,一定要檢查證書有效期,設(shè)置自動(dòng)續(xù)期。我有一次就因?yàn)樽C書過期了,導(dǎo)致全站無法訪問,被老板在群里點(diǎn)名批評……
Step 8:限流防刷——給系統(tǒng)加個(gè)保險(xiǎn)杠
接口被爬蟲狂刷怎么辦?Nginx 可以簡單限流。這里介紹兩種常用的:
限制單 IP 并發(fā)連接數(shù):
# 在 http 塊定義一個(gè)連接限制區(qū)域
limit_conn_zone $binary_remote_addr zone=addr:10m;
server {
location /api/ {
limit_conn addr 10; # 單個(gè) IP 最多 10 個(gè)并發(fā)連接
proxy_pass http://tomcat_cluster;
}
}限制請求速率(漏桶算法):
# 在 http 塊定義
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
server {
location /api/ {
limit_req zone=one burst=20 nodelay; # 每秒 10 個(gè)請求,突發(fā) 20 個(gè)
proxy_pass http://tomcat_cluster;
}
}關(guān)鍵點(diǎn):rate=10r/s 是平均每秒 10 個(gè)請求,burst=20 允許突發(fā) 20 個(gè)請求排隊(duì)處理,nodelay 表示不延遲,直接處理。如果超過了,Nginx 會(huì)返回 503。
限流這玩意兒不能設(shè)太死,不然正常用戶也可能被誤傷。建議先設(shè)寬松一點(diǎn),觀察日志再調(diào)整。
一份生產(chǎn)級精簡配置參考
說了這么多,我把上面這些整合成一份生產(chǎn)可用的精簡配置,你可以直接拿去改改 IP 就能用:
# /etc/nginx/nginx.conf
user nginx;
worker_processes auto; # 根據(jù) CPU 核數(shù)自動(dòng)調(diào)整
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 4096; # 單個(gè) worker 的最大連接數(shù)
use epoll; # Linux 高性能網(wǎng)絡(luò)模型
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# === 日志格式 ===
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
# === 性能優(yōu)化 ===
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
client_max_body_size 50m; # 允許上傳的最大文件大小
# === Gzip 壓縮 ===
gzip on;
gzip_vary on;
gzip_min_length 1k;
gzip_comp_level 4;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
# === 負(fù)載均衡 upstream ===
upstream tomcat_cluster {
least_conn; # 誰閑給誰,比輪詢更智能
server 192.168.1.101:8080 weight=2;
server 192.168.1.102:8080 weight=1;
# 健康檢查:失敗 3 次認(rèn)為不可用,恢復(fù) 2 次認(rèn)為可用
server 192.168.1.101:8080 max_fails=3 fail_timeout=30s;
server 192.168.1.102:8080 max_fails=3 fail_timeout=30s;
}
# === 虛擬主機(jī)配置 ===
server {
listen 80;
server_name api.example.com;
# HTTP 跳轉(zhuǎn) HTTPS(如果不需要 HTTPS 可以注釋掉)
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name api.example.com;
ssl_certificate /etc/nginx/ssl/api.example.com.crt;
ssl_certificate_key /etc/nginx/ssl/api.example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
root /usr/share/nginx/html;
# 靜態(tài)資源直接由 Nginx 處理
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
# 動(dòng)態(tài) API 轉(zhuǎn)發(fā)給 Tomcat 集群
location /api/ {
proxy_pass http://tomcat_cluster;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 連接超時(shí)設(shè)置
proxy_connect_timeout 5s;
proxy_send_timeout 10s;
proxy_read_timeout 30s;
}
}
}這份配置的核心思路:
- Nginx 負(fù)責(zé) SSL 終止、靜態(tài)資源、Gzip 壓縮、負(fù)載均衡
- Tomcat 只專注處理動(dòng)態(tài)業(yè)務(wù)邏輯
- 通過
least_conn和權(quán)重實(shí)現(xiàn)智能分流 - 健康檢查確保單點(diǎn)故障時(shí)自動(dòng)剔除異常節(jié)點(diǎn)
踩坑記錄:我踩過的兩個(gè)大坑
光講配置沒意思,分享兩個(gè)我真實(shí)踩過的坑,說不定你正在踩或者即將踩。
坑一:改完配置沒生效?因?yàn)槟銢] reload!
這個(gè)坑我踩過不下三次。
Nginx 的配置文件改完后,必須執(zhí)行 nginx -s reload 才能熱加載新配置。如果你只改了文件就完事了,Nginx 還在用舊的配置在跑。
更坑的是,有時(shí)候你執(zhí)行了 reload,但語法有錯(cuò)誤,Nginx 會(huì)拒絕加載新配置,然后默默地繼續(xù)用舊配置運(yùn)行。你以為是新配置生效了,實(shí)際上還是老樣子。
正確做法:
# 先檢查語法是否正確 sudo nginx -t # 語法 OK 后再 reload sudo nginx -s reload
養(yǎng)成 nginx -t 的習(xí)慣,能救命。
坑二:location 路徑匹配優(yōu)先級搞錯(cuò)
Nginx 的 location 匹配規(guī)則有點(diǎn)反直覺,不是簡單的"誰在前面先匹配誰"。
它的優(yōu)先級是這樣的:
=精確匹配(最高優(yōu)先級)^~前綴匹配~和~*正則匹配(按配置文件中的順序)- 普通前綴匹配
/通用匹配(最低優(yōu)先級)
有一次我把靜態(tài)資源的正則匹配寫在了 /api/ 的后面,結(jié)果某些帶 .js 后綴的 API 請求被 Nginx 當(dāng)成靜態(tài)資源處理了,直接返回 404,查了半天才發(fā)現(xiàn)是 location 順序的問題。
建議:正則匹配的 location 盡量按精確度從高到低排列,或者直接用 ^~ 做前綴匹配,避免意外。
驗(yàn)證效果:改造前后對比
咱們來驗(yàn)收一下成果。
改造前:
- 單臺 Tomcat 扛所有請求
- 高峰期 CPU 90%+,頻繁 502/504
- 靜態(tài)資源和動(dòng)態(tài)請求混在一起,互相拖累
改造后:
- 兩臺 Tomcat 分擔(dān)動(dòng)態(tài)請求壓力
- Nginx 直接處理靜態(tài)資源,Tomcat CPU 下降約 30%
- 開啟 Gzip 后,靜態(tài)資源體積減少 60%-70%
- 單臺 Tomcat 掛掉時(shí),Nginx 自動(dòng)把流量切到另一臺,服務(wù)不中斷
雖然架構(gòu)還是很簡單,但對于中小型項(xiàng)目來說,這套方案性價(jià)比極高,花半天時(shí)間配置,能換來很長一段時(shí)間的安穩(wěn) sleep。
總結(jié)
今天咱們聊了怎么用 Nginx 解決"一臺服務(wù)器扛不住"的問題:
- 反向代理就像是餐廳門口的服務(wù)員,用戶只跟 Nginx 打交道,后端 Tomcat 被隱藏起來
- 負(fù)載均衡讓多臺 Tomcat 一起干活,策略有輪詢、權(quán)重、ip_hash、least_conn,按需選擇
- 靜態(tài)資源分離能顯著減輕 Tomcat 負(fù)擔(dān),讓專業(yè)的人干專業(yè)的事
- Gzip 壓縮和 HTTPS 配置是現(xiàn)代 Web 服務(wù)的基本操作
- 限流防刷能給系統(tǒng)加一道保險(xiǎn)杠,防止被惡意流量沖垮
當(dāng)然,這套方案也不是銀彈。如果業(yè)務(wù)量繼續(xù)增長,后面你可能還要引入 Redis 做分布式 Session、用 Consul 做服務(wù)發(fā)現(xiàn)、上 Kubernetes 做容器編排……但那是后話了。
千里之行,始于一個(gè)靠譜的 Nginx 配置。
到此這篇關(guān)于Nginx 反向代理與負(fù)載均衡一臺服務(wù)器扛不住怎么辦的文章就介紹到這了,更多相關(guān)Nginx 反向代理與負(fù)載均衡內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 使用Nginx實(shí)現(xiàn)反向代理、配置負(fù)載均衡詳解
- Nginx實(shí)現(xiàn)負(fù)載均衡和反向代理的方法
- nginx?反向代理負(fù)載均衡策略配置SSL訪問匹配規(guī)則優(yōu)先級
- Nginx反向代理與負(fù)載均衡概念理解及模塊使用
- springboot整合Nginx實(shí)現(xiàn)負(fù)載均衡反向代理的方法詳解
- Nginx反向代理及負(fù)載均衡如何實(shí)現(xiàn)(基于linux)
- Nginx配置參數(shù)中文說明詳解(負(fù)載均衡與反向代理)
- Nginx正反向代理及負(fù)載均衡等功能實(shí)現(xiàn)配置代碼實(shí)例
相關(guān)文章
CentOS6使用nginx搭建web網(wǎng)站服務(wù)的方法
這篇文章主要介紹了CentOS6使用nginx搭建web網(wǎng)站服務(wù)的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-07-07
接口服務(wù)在Nginx中提示HTTP 499問題的排查步驟
本文詳細(xì)介紹了如何在Nginx中啟用請求時(shí)間日志以及如何在沒有該日志的情況下通過替代方法排查HTTP499問題,重點(diǎn)討論了前端超時(shí)配置差異和請求參數(shù)導(dǎo)致的文件大小差異,并提供了具體的排查步驟,需要的朋友可以參考下2026-03-03
解決nginx服務(wù)器上發(fā)布的新版本代碼總需要清除瀏覽器緩存問題
這篇文章主要介紹了解決nginx服務(wù)器上發(fā)布的新版本代碼總需要清除瀏覽器緩存問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01
Nginx反向代理之proxy_redirect指令的實(shí)現(xiàn)
proxy_redirect指令是用來重置頭信息中的"Location"和"Refresh"的值,本文就來詳細(xì)的介紹一下如何使用,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08
Nginx配置https原理及實(shí)現(xiàn)過程詳解
這篇文章主要介紹了Nginx配置https原理及實(shí)現(xiàn)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09

