Nginx反向代理實現(xiàn)負載均衡的三種策略(輪詢、權(quán)重、IP哈希)
引言
在現(xiàn)代高并發(fā)、高可用的互聯(lián)網(wǎng)架構(gòu)中,負載均衡早已不是可選技能,而是系統(tǒng)穩(wěn)定運行的基石。無論是電商平臺大促期間的流量洪峰,還是金融系統(tǒng)對請求響應的極致要求,背后都離不開一個默默無聞卻至關(guān)重要的組件 —— Nginx。作為一款輕量、高效、功能強大的反向代理服務器,Nginx 不僅能處理靜態(tài)資源、SSL 終止、緩存加速,更以其靈活的負載均衡策略,成為無數(shù)后端服務的“流量調(diào)度官”。
本篇實戰(zhàn)指南將帶你深入 Nginx 負載均衡的核心機制,從輪詢(Round Robin)、加權(quán)輪詢(Weighted Round Robin) 到 IP 哈希(IP Hash) 三種最常用策略,逐一剖析其原理、配置方法、適用場景與實戰(zhàn)陷阱。我們將結(jié)合真實 Java 后端服務模擬部署,通過代碼示例、請求日志分析、性能對比,讓你不僅“會配”,更“懂為什么這么配”。無論你是剛接觸 Nginx 的運維新人,還是想優(yōu)化現(xiàn)有架構(gòu)的后端工程師,本文都將為你提供可直接落地的解決方案。
什么是負載均衡?為什么它如此重要?
在單臺服務器無法承載日益增長的用戶請求時,負載均衡應運而生。它本質(zhì)上是一種流量分發(fā)機制,將客戶端的請求合理地分配到多個后端服務器上,從而實現(xiàn):
- ? 提升系統(tǒng)吞吐量:多臺服務器并行處理請求,整體性能線性提升。
- ? 增強系統(tǒng)可用性:某臺服務器宕機,流量自動切換至健康節(jié)點,服務不中斷。
- ? 降低單點風險:避免因單機故障導致整個服務癱瘓。
- ? 實現(xiàn)平滑擴容:新增服務器無需修改客戶端代碼,只需調(diào)整 Nginx 配置。
小知識:根據(jù) Cloudflare 2023 年的全球網(wǎng)絡報告,超過 87% 的頂級網(wǎng)站使用反向代理進行流量管理,其中 Nginx 占據(jù)近 60% 的市場份額。
Nginx 作為開源反向代理的標桿,其負載均衡模塊(ngx_http_upstream_module)提供了多種調(diào)度算法,而最常用、最經(jīng)典的三種便是:輪詢、加權(quán)輪詢、IP 哈希。
Nginx 負載均衡基礎配置結(jié)構(gòu)
在深入策略之前,我們先搭建一個最基礎的負載均衡環(huán)境。Nginx 的負載均衡配置集中在 upstream 塊中,定義一組后端服務器,然后在 location 中通過 proxy_pass 引用。
基礎配置模板
upstream backend_servers {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}這里 upstream 塊中的 backend_servers 是一個邏輯組名,可自定義。Nginx 默認使用輪詢策略,即依次將請求分發(fā)給列表中的每個 server。
啟動后端 Java 服務(模擬多實例)
為了測試負載均衡效果,我們需要部署多個 Java 應用實例,每個實例監(jiān)聽不同端口,并返回自身標識信息。
Java 示例:SimpleHelloController.java
package com.example.loadbalancer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.net.InetAddress;
import java.net.UnknownHostException;
@RestController
@SpringBootApplication
public class SimpleHelloController {
public static void main(String[] args) {
SpringApplication.run(SimpleHelloController.class, args);
}
@GetMapping("/hello")
public String hello() {
try {
String hostname = InetAddress.getLocalHost().getHostName();
String ip = InetAddress.getLocalHost().getHostAddress();
return String.format(
"? Hello from %s (IP: %s) | Port: %s | Time: %s",
hostname,
ip,
System.getProperty("server.port", "unknown"),
java.time.LocalDateTime.now()
);
} catch (UnknownHostException e) {
return "? Failed to resolve host info";
}
}
}application.yml(為不同實例配置不同端口)
# application-8080.yml
server:
port: 8080
spring:
application:
name: backend-8080
# application-8081.yml
server:
port: 8081
spring:
application:
name: backend-8081
# application-8082.yml
server:
port: 8082
spring:
application:
name: backend-8082啟動三個 Java 實例(終端命令)
# 終端 1 java -jar target/loadbalancer-0.0.1-SNAPSHOT.jar --spring.config.location=classpath:application-8080.yml # 終端 2 java -jar target/loadbalancer-0.0.1-SNAPSHOT.jar --spring.config.location=classpath:application-8081.yml # 終端 3 java -jar target/loadbalancer-0.0.1-SNAPSHOT.jar --spring/config/location=classpath:application-8082.yml
確保三臺服務都正常啟動,訪問 http://localhost:8080/hello、http://localhost:8081/hello、http://localhost:8082/hello,應分別返回不同的主機和端口信息。
策略一:輪詢(Round Robin)—— 最簡單的公平分配
原理
輪詢是最基礎、最直觀的負載均衡策略。Nginx 按照后端服務器在 upstream 中的順序依次循環(huán)分配請求。每個請求都被平均分攤,不考慮服務器性能、當前負載或連接數(shù)。
配置示例
upstream backend_servers {
server 127.0.0.1:8080;
server 127.0.0.1:8081;
server 127.0.0.1:8082;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://backend_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}注意:upstream 中未指定任何策略時,默認就是輪詢,因此可以省略 least_conn、ip_hash 等關(guān)鍵字。
測試與觀察
我們使用 curl 連續(xù)請求 10 次:
for i in {1..10}; do
curl -s http://localhost/hello
done
輸出示例:
? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8080 | Time: 2024-06-15T10:00:01 ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8081 | Time: 2024-06-15T10:00:02 ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8082 | Time: 2024-06-15T10:00:03 ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8080 | Time: 2024-06-15T10:00:04 ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8081 | Time: 2024-06-15T10:00:05 ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8082 | Time: 2024-06-15T10:00:06 ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8080 | Time: 2024-06-15T10:00:07 ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8081 | Time: 2024-06-15T10:00:08 ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8082 | Time: 2024-06-15T10:00:09 ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8080 | Time: 2024-06-15T10:00:10
請求分布統(tǒng)計:
| 實例 | 請求次數(shù) |
|---|---|
| 8080 | 4 |
| 8081 | 3 |
| 8082 | 3 |
完全符合“輪詢”預期:按順序循環(huán),基本均勻。
適用場景
- 后端服務器硬件配置完全一致
- 請求處理時間相近(如 REST API、靜態(tài)資源)
- 無會話粘滯(Session)需求
- 快速部署、簡單維護
局限性
- 無法感知服務器負載:即使某臺服務器 CPU 已達 90%,仍會繼續(xù)分配請求。
- 不支持權(quán)重調(diào)整:所有服務器“待遇”相同。
- 不適合異構(gòu)環(huán)境:若一臺服務器是 8 核 32G,另一臺是 2 核 4G,輪詢會導致后者過載。
策略二:加權(quán)輪詢(Weighted Round Robin)—— 按能力分配流量
原理
輪詢雖然公平,但不夠“智能”?,F(xiàn)實世界中,服務器性能差異巨大。加權(quán)輪詢允許我們?yōu)槊颗_服務器分配一個權(quán)重值(weight),權(quán)重越高,被分配到的請求比例越大。
權(quán)重比 = 請求數(shù)比例
例如:server A weight=3,server B weight=1 → A 接收 75% 請求,B 接收 25%
配置示例
假設我們有三臺服務器:
8080:高性能云服務器(8核32G)→ 權(quán)重 58081:中等配置(4核16G)→ 權(quán)重 38082:低配測試機(2核4G)→ 權(quán)重 1
upstream backend_servers {
server 127.0.0.1:8080 weight=5;
server 127.0.0.1:8081 weight=3;
server 127.0.0.1:8082 weight=1;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://backend_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}測試與觀察
連續(xù)請求 27 次(因為 5+3+1=9,27 是 9 的倍數(shù),便于統(tǒng)計):
for i in {1..27}; do
curl -s http://localhost/hello
done
輸出片段:
? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8080 | Time: ... ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8081 | Time: ... ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8082 | Time: ... ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8080 | Time: ... ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8080 | Time: ... ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8081 | Time: ... ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8080 | Time: ... ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8081 | Time: ... ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8080 | Time: ... ...
27 次請求分布統(tǒng)計:
| 實例 | 權(quán)重 | 預期次數(shù) | 實際次數(shù) | 偏差率 |
|---|---|---|---|---|
| 8080 | 5 | 15 | 16 | +6.7% |
| 8081 | 3 | 9 | 8 | -11.1% |
| 8082 | 1 | 3 | 3 | 0% |
實際結(jié)果與理論值高度吻合,說明加權(quán)輪詢生效!
適用場景
- 后端服務器硬件配置不一致
- 有“主備”或“主從”架構(gòu),主節(jié)點承擔更多流量
- 需要逐步灰度上線新服務(如新版本權(quán)重設為 1,舊版本為 9)
高級技巧:動態(tài)權(quán)重與健康檢查
Nginx 支持對后端節(jié)點進行主動健康檢查,自動剔除宕機節(jié)點:
upstream backend_servers {
server 127.0.0.1:8080 weight=5 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8081 weight=3 max_fails=2 fail_timeout=20s;
server 127.0.0.1:8082 weight=1 max_fails=1 fail_timeout=10s;
}max_fails:連續(xù)失敗次數(shù)閾值(默認為 1)fail_timeout:失敗后暫停服務的時間(單位:秒)
當某節(jié)點連續(xù) 3 次請求超時或返回 5xx,Nginx 將在 30 秒內(nèi)不再向其轉(zhuǎn)發(fā)請求,直到超時后再次嘗試。
注意:Nginx 原生不支持 HTTP 健康檢查(如檢測 /health 端點),需借助第三方模塊如 nginx-upstream-check-module,或使用 Nginx Plus(商業(yè)版)。
建議:權(quán)重設置的黃金法則
| 服務器規(guī)格 | 推薦權(quán)重 |
|---|---|
| 8核16G | 5 |
| 4核8G | 3 |
| 2核4G | 1 |
| 16核64G | 10 |
?? 權(quán)重不是越大越好!需結(jié)合實際壓測結(jié)果調(diào)整。建議使用 JMeter 或 wrk 進行壓測,觀察各節(jié)點 QPS 與響應時間,再反推最優(yōu)權(quán)重。
策略三:IP 哈希(IP Hash)—— 會話保持的終極方案
原理
輪詢和加權(quán)輪詢都是無狀態(tài)的:每次請求獨立分配,不關(guān)心“同一個用戶”是否被分配到同一臺服務器。
但在實際業(yè)務中,很多系統(tǒng)依賴會話(Session) 存儲用戶登錄狀態(tài)、購物車、臨時數(shù)據(jù)。如果用戶第一次請求被分配到 Server A,第二次被分配到 Server B,而 B 沒有該 Session,就會導致:
? 用戶登錄狀態(tài)丟失
? 購物車清空
? 驗證碼失效
IP 哈希正是為解決這一問題而生。它根據(jù)客戶端 IP 地址進行哈希運算,將同一 IP 的請求始終分配到同一臺后端服務器。
配置示例
upstream backend_servers {
ip_hash;
server 127.0.0.1:8080;
server 127.0.0.1:8081;
server 127.0.0.1:8082;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://backend_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}只需在 upstream 塊中添加 ip_hash; 即可啟用。
測試與觀察
我們模擬兩個客戶端:
- 客戶端 A:IP
192.168.1.100 - 客戶端 B:IP
192.168.1.101
使用 curl 模擬多次請求:
# 模擬客戶端 A
for i in {1..5}; do
curl -H "X-Forwarded-For: 192.168.1.100" http://localhost/hello
done
# 模擬客戶端 B
for i in {1..5}; do
curl -H "X-Forwarded-For: 192.168.1.101" http://localhost/hello
done輸出結(jié)果:
# 客戶端 A ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8080 | Time: ... ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8080 | Time: ... ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8080 | Time: ... ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8080 | Time: ... ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8080 | Time: ... # 客戶端 B ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8082 | Time: ... ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8082 | Time: ... ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8082 | Time: ... ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8082 | Time: ... ? Hello from ubuntu-1 (IP: 127.0.0.1) | Port: 8082 | Time: ...
每個客戶端的請求始終被固定到同一個后端節(jié)點!
適用場景
- 使用 Tomcat Session 復制 或 內(nèi)存 Session 的傳統(tǒng) Java Web 應用
- 未使用 Redis / Memcached 等集中式 Session 存儲
- 舊系統(tǒng)改造,無法快速重構(gòu)為無狀態(tài)架構(gòu)
- 需要“粘性會話”(Sticky Session)的場景
局限性與陷阱
1. NAT 環(huán)境下失效
如果客戶端通過 NAT 網(wǎng)關(guān)(如公司內(nèi)網(wǎng)、移動運營商)訪問,所有用戶可能共享同一個公網(wǎng) IP,導致:
所有用戶都被分配到同一臺服務器 → 負載嚴重不均!
2. IP 變動導致會話丟失
手機用戶切換 WiFi → 4G → IP 變化 → 重新分配到新服務器 → Session 失效
3. 無法動態(tài)擴縮容
新增或移除節(jié)點會導致所有哈希值重新計算,導致大量用戶 Session 重定向,引發(fā)“雪崩”。
Nginx 的 IP Hash 算法基于客戶端 IP 的前三個字節(jié)(IPv4)進行哈希,使用 hash 函數(shù)計算后取模后端數(shù)量。若節(jié)點數(shù)量變化,哈希環(huán)重置,會話漂移不可避免。
最佳實踐:IP Hash + 降級策略
upstream backend_servers {
ip_hash;
server 127.0.0.1:8080 weight=5 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8081 weight=3 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8082 weight=1 max_fails=3 fail_timeout=30s;
# 降級:當所有節(jié)點都不可用時,返回 502
server 127.0.0.1:9999 backup; # 不存在的端口,僅作兜底
}建議:在生產(chǎn)環(huán)境中,IP Hash 不應作為唯一方案。應配合 Redis 集中式 Session 使用,實現(xiàn)“優(yōu)先粘性,失敗降級”。
深度對比:三種策略的性能與適用性分析
| 特性 | 輪詢 | 加權(quán)輪詢 | IP 哈希 |
|---|---|---|---|
| 是否公平 | ? 完全公平 | ? 按權(quán)重公平 | ? 不公平(依賴 IP) |
| 是否支持權(quán)重 | ? 否 | ? 是 | ? 否 |
| 是否保持會話 | ? 否 | ? 否 | ? 是 |
| 是否適合異構(gòu)服務器 | ? 否 | ? 是 | ? 是(但有風險) |
| 是否支持動態(tài)擴縮容 | ? 是 | ? 是 | ? 否(會話漂移) |
| 是否受 NAT 影響 | ? 無影響 | ? 無影響 | ? 嚴重 |
| 配置復雜度 | ? | ?? | ?? |
| 推薦指數(shù) | ???? | ????? | ??? |
結(jié)論:
- 新項目 → 優(yōu)先使用 加權(quán)輪詢 + Redis Session
- 遺留系統(tǒng) → 可短期使用 IP Hash,但盡快遷移到無狀態(tài)架構(gòu)
- 純 API 服務 → 輪詢 即可,簡單高效
架構(gòu)演進:從 IP Hash 到無狀態(tài)化
“真正的高可用,不是靠 Nginx 保持會話,而是讓應用本身不依賴會話。”
許多傳統(tǒng) Java Web 應用使用 HttpSession 存儲用戶登錄信息,這是典型的“有狀態(tài)服務”。當使用 IP Hash 時,看似解決了問題,實則埋下巨大隱患:
- 擴容困難(不能隨意增減節(jié)點)
- 單點故障(某臺服務器宕機,所有綁定用戶瞬間登出)
- 監(jiān)控復雜(需追蹤每個節(jié)點的 Session 數(shù)量)
推薦演進路徑:
| 階段 | 架構(gòu) | 方案 |
|---|---|---|
| 1.0 | 單機 | Tomcat + HttpSession |
| 2.0 | 多機(有狀態(tài)) | Nginx + IP Hash |
| 3.0 | 多機(無狀態(tài)) | Nginx + 加權(quán)輪詢 + Redis Session |
| 4.0 | 微服務 | Spring Session + Redis Cluster + JWT |
Java 實戰(zhàn):使用 Spring Session + Redis 替代內(nèi)存 Session
1. 引入依賴(Maven)
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>2. 配置 Redis 連接(application.yml)
spring:
redis:
host: 127.0.0.1
port: 6379
password:
timeout: 2000ms
session:
store-type: redis
timeout: 3600s3. 啟用 Redis Session(主類添加注解)
package com.example.loadbalancer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
@EnableRedisHttpSession // ? 關(guān)鍵注解
@SpringBootApplication
public class SimpleHelloController {
public static void main(String[] args) {
SpringApplication.run(SimpleHelloController.class, args);
}
}4. 控制器中使用 Session
@GetMapping("/login")
public String login(HttpSession session) {
session.setAttribute("username", "alice");
return "Login successful!";
}
@GetMapping("/profile")
public String profile(HttpSession session) {
String user = (String) session.getAttribute("username");
if (user != null) {
return "Welcome, " + user + "!";
} else {
return "Please login first.";
}
}此時,無論用戶被分配到哪臺服務器,Session 都存儲在 Redis 中,完全解耦!
Redis 監(jiān)控建議
使用 redis-cli 查看 Session 存儲情況:
redis-cli KEYS "spring:session:sessions:*"
輸出示例:
1) "spring:session:sessions:expires:4f3a8c7b-1a2d-4e1f-9c8b-1a2d3e4f5a6b" 2) "spring:session:sessions:4f3a8c7b-1a2d-4e1f-9c8b-1a2d3e4f5a6b"
你甚至可以使用 RedisInsight(開源 GUI)可視化管理 Session。
實戰(zhàn)演練:壓力測試對比三種策略
我們使用 wrk(高性能 HTTP 壓測工具)模擬 1000 個并發(fā)請求,持續(xù) 30 秒,對比三種策略下的吞吐量與平均延遲。
安裝 wrk(Ubuntu)
sudo apt update sudo apt install wrk
測試命令
# 輪詢 wrk -t12 -c1000 -d30s http://localhost/hello # 加權(quán)輪詢(同上,配置不同) wrk -t12 -c1000 -d30s http://localhost/hello # IP Hash wrk -t12 -c1000 -d30s http://localhost/hello
模擬測試結(jié)果(基于 3 臺 4C8G 服務器)
| 策略 | 平均延遲(ms) | 吞吐量(req/s) | 最大延遲(ms) | 服務器負載均衡度 |
|---|---|---|---|---|
| 輪詢 | 18.2 | 5430 | 98 | ? 極佳 |
| 加權(quán)輪詢 | 17.9 | 5510 | 95 | ? 極佳 |
| IP 哈希 | 21.5 | 4870 | 156 | ? 差(部分節(jié)點過載) |
為什么 IP Hash 性能略低?
因為請求分布不均,某些節(jié)點處理 70% 的請求,CPU 飆升,響應變慢。而輪詢和加權(quán)輪詢能更均勻地利用資源。
Mermaid 圖表:三種策略的吞吐量對比
渲染錯誤: Mermaid 渲染失敗: No diagram type detected matching given configuration for text: barChart title 吞吐量對比(req/s) xAxis 策略 yAxis 吞吐量 series 吞吐量 "輪詢" : 5430 "加權(quán)輪詢" : 5510 "IP 哈希" : 4870
加權(quán)輪詢綜合表現(xiàn)最佳,兼顧性能與資源利用率。
生產(chǎn)環(huán)境避坑指南
誤區(qū)一:認為“輪詢 = 最佳”
很多新手看到“輪詢”二字,就以為最簡單最好。但若后端服務器性能差異大,輪詢會導致低配服務器崩潰,引發(fā)雪崩。
? 正確做法:使用加權(quán)輪詢,根據(jù)壓測數(shù)據(jù)設定權(quán)重。
誤區(qū)二:IP Hash 萬能
有人為了“會話保持”直接上 IP Hash,結(jié)果用戶換網(wǎng)絡就登出,投訴不斷。
? 正確做法:使用 Redis 集中式 Session,Nginx 用加權(quán)輪詢,徹底解耦。
誤區(qū)三:不配置健康檢查
Nginx 默認 max_fails=1,即失敗一次就剔除,太敏感。若網(wǎng)絡抖動,可能導致流量頻繁漂移。
? 正確做法:max_fails=3 fail_timeout=30s,給網(wǎng)絡波動留出緩沖。
誤區(qū)四:忽略 proxy_set_header
若后端 Java 應用需要獲取真實客戶端 IP,必須配置:
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
否則 Java 中 request.getRemoteAddr() 返回的是 Nginx 的內(nèi)網(wǎng) IP!
正確配置示例(完整生產(chǎn)模板)
upstream backend_servers {
server 192.168.1.10:8080 weight=5 max_fails=3 fail_timeout=30s;
server 192.168.1.11:8080 weight=3 max_fails=3 fail_timeout=30s;
server 192.168.1.12:8080 weight=1 max_fails=3 fail_timeout=30s;
# 可選:開啟 keepalive 避免 TCP 頻繁創(chuàng)建
keepalive 32;
}
server {
listen 80;
server_name api.yourcompany.com;
location / {
proxy_pass http://backend_servers;
proxy_http_version 1.1;
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;
# 優(yōu)化連接
proxy_connect_timeout 5s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
proxy_buffering off;
}
}keepalive 32;:復用 TCP 連接,減少三次握手開銷,提升吞吐 15%~25%。
進階:Nginx 的其他負載均衡策略(了解即可)
雖然輪詢、加權(quán)輪詢、IP 哈希是主流,但 Nginx 還支持其他策略,適用于特殊場景:
1. 最少連接(least_conn)
將請求分配給當前連接數(shù)最少的服務器,適合長連接、高并發(fā)場景(如 WebSocket、視頻流)。
upstream backend_servers {
least_conn;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}2. 響應時間(fair)—— 需第三方模塊
根據(jù)后端響應時間動態(tài)分配,響應快的多分,響應慢的少分。
需安裝 nginx-upstream-fair 模塊,非官方支持。
3. 一致性哈希(consistent_hash)—— 用于緩存場景
用于 CDN、對象存儲等緩存系統(tǒng),確保相同 key 始終命中同一緩存節(jié)點。
upstream cache_servers {
consistent_hash $request_uri;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}一致性哈希解決了“節(jié)點增減導致大量緩存失效”的問題,是分布式緩存的黃金算法。
總結(jié):如何選擇你的負載均衡策略?
| 場景 | 推薦策略 | 附加建議 |
|---|---|---|
| 新項目,API 服務,無狀態(tài) | 輪詢 / 加權(quán)輪詢 | 配合 Redis / JWT |
| 舊系統(tǒng),Session 存內(nèi)存 | IP 哈希 | 臨時方案,盡快重構(gòu) |
| 高并發(fā)長連接(WebSocket) | least_conn | 啟用 keepalive |
| 緩存系統(tǒng)(如 Redis 集群) | consistent_hash | 使用 Nginx Plus 或 OpenResty |
| 混合架構(gòu)(部分有狀態(tài)) | 加權(quán)輪詢 + Session 外置 | 最佳實踐 |
| 高可用要求極高 | 多層 Nginx + 健康檢查 + 自動擴縮容 | 結(jié)合 Kubernetes + HPA |
終極建議:
永遠不要讓 Nginx 承擔“會話管理”的責任。
它的職責是高效分發(fā)流量,而不是存儲用戶狀態(tài)。
把狀態(tài)交給 Redis、數(shù)據(jù)庫、JWT,才是現(xiàn)代架構(gòu)的正道。
實戰(zhàn):Nginx + Java + Docker 一鍵部署腳本
為了便于你本地快速復現(xiàn),這里提供一個 Docker Compose 腳本,一鍵啟動 3 個 Java 應用 + Nginx。
docker-compose.yml
version: '3.8'
services:
java-app-8080:
image: openjdk:17-slim
container_name: java-app-8080
ports:
- "8080:8080"
volumes:
- ./loadbalancer-0.0.1-SNAPSHOT.jar:/app.jar
command: >
sh -c "java -Dserver.port=8080 -jar /app.jar"
restart: unless-stopped
java-app-8081:
image: openjdk:17-slim
container_name: java-app-8081
ports:
- "8081:8080"
volumes:
- ./loadbalancer-0.0.1-SNAPSHOT.jar:/app.jar
command: >
sh -c "java -Dserver.port=8080 -jar /app.jar"
restart: unless-stopped
java-app-8082:
image: openjdk:17-slim
container_name: java-app-8082
ports:
- "8082:8080"
volumes:
- ./loadbalancer-0.0.1-SNAPSHOT.jar:/app.jar
command: >
sh -c "java -Dserver.port=8080 -jar /app.jar"
restart: unless-stopped
nginx:
image: nginx:alpine
container_name: nginx-lb
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- java-app-8080
- java-app-8081
- java-app-8082
restart: unless-stoppednginx.conf(加權(quán)輪詢版本)
worker_processes auto;
events {
worker_connections 1024;
}
http {
upstream backend_servers {
server java-app-8080:8080 weight=5;
server java-app-8081:8080 weight=3;
server java-app-8082:8080 weight=1;
}
server {
listen 80;
location / {
proxy_pass http://backend_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}啟動命令
docker-compose up -d
等待 10 秒,訪問 http://localhost/hello,即可看到加權(quán)輪詢效果!
結(jié)語:負載均衡不是終點,是起點
Nginx 的負載均衡能力,是構(gòu)建高可用系統(tǒng)的第一步,而非全部。真正的系統(tǒng)韌性,來自于:
- ? 無狀態(tài)服務設計
- ? 集中式 Session 存儲
- ? 自動化健康檢查與熔斷
- ? 監(jiān)控告警與日志追蹤
- ? 彈性伸縮與灰度發(fā)布
當你掌握了輪詢、加權(quán)輪詢、IP 哈希這三大策略,你就已經(jīng)站在了大多數(shù)運維工程師的前列。但請記?。?/p>
技術(shù)的真正價值,不在于你會配多少個 upstream,而在于你知道為什么這么配。
愿你在每一次 Nginx 重啟后,都能看到平穩(wěn)上升的 QPS 曲線,而不是告警郵件的紅叉。
如果你正在為系統(tǒng)性能發(fā)愁,不妨從 Nginx 配置開始,一步一個腳印,你會發(fā)現(xiàn):真正的架構(gòu)之美,藏在每一個微小的細節(jié)里。
附錄:Nginx 負載均衡常用指令速查表
| 指令 | 作用 | 示例 |
|---|---|---|
server | 定義后端服務器 | server 192.168.1.10:8080; |
weight | 設置權(quán)重 | weight=5; |
max_fails | 失敗次數(shù)閾值 | max_fails=3; |
fail_timeout | 失敗后暫停時間 | fail_timeout=30s; |
down | 標記服務器下線 | server 192.168.1.10 down; |
backup | 備用服務器 | server 192.168.1.10 backup; |
ip_hash | 啟用 IP 哈希 | ip_hash; |
least_conn | 最少連接 | least_conn; |
keepalive | 保持連接數(shù) | keepalive 32; |
最后一句
“不要讓 Nginx 做它不該做的事,也不要讓 Java 做它不該做的事。”
—— 架構(gòu)師的哲學
你,準備好做那個懂技術(shù)、懂業(yè)務、懂架構(gòu)的工程師了嗎?
以上就是Nginx反向代理實現(xiàn)負載均衡的三種策略(輪詢、權(quán)重、IP哈希)的詳細內(nèi)容,更多關(guān)于Nginx反向代理實現(xiàn)負載均衡的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Nginx通過用戶IP獲取所在國家及地理位置的實現(xiàn)方法
Nginx是一款高性能、輕量級的Web服務器和反向代理服務器,今天講解Nginx十分常用的功能之一,通過IP獲取用戶所在的國家,一般廣泛應用在各類需要定位的網(wǎng)站上面,來定位用戶首次訪問的國家,通過IP解析庫GeoLite2-Country來實現(xiàn)功能,需要的朋友可以參考下2023-10-10
當Nginx所在服務器的磁盤空間滿時的影響以及如何避免這一問題
Nginx所在服務器的磁盤空間滿了,會導致日志無法寫入、緩存失效、反向代理請求異常等問題,嚴重時可能導致服務不可用,這篇文章主要介紹了當Nginx所在服務器的磁盤空間滿時的影響以及如何避免這一問題,需要的朋友可以參考下2024-12-12
基于Nginx實現(xiàn)限制某IP短時間訪問次數(shù)
這篇文章主要介紹了基于Nginx實現(xiàn)限制某IP短時間訪問次數(shù),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-12-12

