Nginx反向代理+负载均衡实战:小白也能搭建高可用架构
一、概述
1.1 背景介绍
2023年我带了个新人,让他搭个负载均衡,结果他配了半天,流量全跑到一台机器上,另外两台闲着。排查了一个小时,发现他把upstream里的server全写成了localhost。这事儿让我意识到,反向代理和负载均衡看着简单,坑还真不少。
对于很多刚入行的同学来说,"反向代理"这词听着就让人头大。其实没那么复杂:正向代理是代理客户端(比如VPN),反向代理是代理服务端。用户访问的是Nginx,Nginx再去访问真正的后端服务,用户根本不知道后端服务的存在。
负载均衡则是反向代理的进阶玩法。当一台后端服务器扛不住流量的时候,就需要多台服务器一起分担压力。Nginx负责把请求分发到不同的服务器上,这就是负载均衡。
1.2 技术特点
反向代理的价值:
隐藏后端服务器真实IP,增强安全性
统一入口,便于管理和监控
SSL卸载,减轻后端压力
静态资源缓存,减少后端请求
请求过滤和限流
负载均衡的价值:
水平扩展,突破单机性能瓶颈
高可用,单点故障不影响服务
灵活调度,支持多种算法
灰度发布,逐步迁移流量
1.3 适用场景
Web应用集群部署
API网关入口
微服务架构流量分发
静态资源与动态请求分离
多数据中心流量调度
蓝绿部署和灰度发布
1.4 环境要求
| 组件 | 版本 | 说明 |
|---|---|---|
| 操作系统 | Rocky Linux 9.4 / Ubuntu 24.04 LTS | 稳定的生产环境系统 |
| Nginx | 1.26.2 | 建议使用stable分支 |
| 后端服务 | 任意HTTP服务 | 示例用Tomcat 10.1.x |
| 网络 | 内网互通 | Nginx与后端服务器网络延迟<1ms |
实验环境架构:
┌─────────────────┐ │ Client请求 │ └────────┬────────┘ │ ▼ ┌─────────────────┐ │ Nginx负载均衡 │ │ 192.168.1.10 │ └────────┬────────┘ │ ┌─────────────────┼─────────────────┐ │ │ │ ▼ ▼ ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Backend 1 │ │ Backend 2 │ │ Backend 3 │ │ 192.168.1.11│ │ 192.168.1.12│ │ 192.168.1.13│ └─────────────┘ └─────────────┘ └─────────────┘
二、详细步骤
2.1 准备工作
2.1.1 安装Nginx
Rocky Linux 9:
# 添加Nginx官方仓库 cat > /etc/yum.repos.d/nginx.repo << 'EOF' [nginx-stable] name=nginx stable repo baseurl=http://nginx.org/packages/centos/$releasever/$basearch/ gpgcheck=1 enabled=1 gpgkey=https://nginx.org/keys/nginx_signing.key module_hotfixes=true EOF # 安装Nginx dnf install nginx -y # 启动并设置开机自启 systemctl start nginx systemctl enable nginx # 检查版本 nginx -v
Ubuntu 24.04:
# 安装依赖 apt update apt install curl gnupg2 ca-certificates lsb-release ubuntu-keyring -y # 添加Nginx官方GPG密钥 curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor | tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null # 添加仓库 echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" | tee /etc/apt/sources.list.d/nginx.list # 安装 apt update apt install nginx -y # 启动 systemctl start nginx systemctl enable nginx
2.1.2 准备后端服务
这里用一个简单的Python HTTP服务来模拟后端,方便看到请求分发到了哪台机器:
#!/usr/bin/env python3
# backend_server.py
import http.server
import socketserver
import socket
PORT = 8080
HOSTNAME = socket.gethostname()
IP = socket.gethostbyname(HOSTNAME)
class MyHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
response = f"""
Hello from Backend Server
Hostname: {HOSTNAME}
IP: {IP}
Port: {PORT}
Request Path: {self.path}
"""
self.wfile.write(response.encode())
with socketserver.TCPServer(("", PORT), MyHandler) as httpd:
print(f"Serving on port {PORT}")
httpd.serve_forever()
在三台后端服务器上分别启动:
python3 backend_server.py
或者如果你有Tomcat环境,直接用Tomcat也行:
# 修改不同服务器的端口,便于区分 # server.xml中修改HTTP Connector端口 # 分别启动三台Tomcat
2.2 核心配置
2.2.1 最简单的反向代理
先从最简单的配置开始,让Nginx把请求转发到后端服务:
# /etc/nginx/conf.d/proxy.conf
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://192.168.1.11:8080;
}
}
测试一下:
curl http://192.168.1.10
你会看到返回的是后端服务器的响应。这就是最基础的反向代理,就这么简单。
2.2.2 完善的反向代理配置
实际生产中,光一个proxy_pass肯定不够。下面是生产环境必须配置的参数:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://192.168.1.11:8080;
# 代理头设置(重要!)
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_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# 缓冲设置
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 32k;
proxy_busy_buffers_size 64k;
}
}
几个关键配置解释:
proxy_set_header Host $host:把客户端请求的Host头传给后端。不设置这个,后端收到的Host是upstream的地址,很多应用会出问题。
X-Real-IP 和 X-Forwarded-For:让后端知道真实的客户端IP。不设置的话,后端看到的都是Nginx的IP。
proxy_buffering:开启缓冲后,Nginx会先把后端响应存到缓冲区,再发给客户端。这样即使客户端网速慢,也不会一直占用后端连接。
踩坑记录:有次我们的后端应用一直获取不到客户端真实IP,日志里全是Nginx的内网IP。排查半天,发现是忘了配X-Forwarded-For头。后端应用也需要配置信任代理地址才能正确解析这个头。
2.2.3 基础负载均衡
现在来配置负载均衡,把请求分发到多台后端服务器:
# /etc/nginx/conf.d/loadbalance.conf
# 定义后端服务器组
upstream backend_pool {
server 192.168.1.11:8080;
server 192.168.1.12:8080;
server 192.168.1.13:8080;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend_pool;
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;
}
}
测试负载均衡效果:
# 连续发送多个请求,观察响应中的服务器IP变化
for i in {1..10}; do
curl -s http://192.168.1.10 | grep "IP:"
done
你会看到请求被轮流分发到三台服务器上,这就是默认的轮询(Round Robin)算法。
2.2.4 负载均衡算法详解
Nginx支持多种负载均衡算法,选择合适的算法对系统性能影响很大。
1. 轮询(Round Robin)- 默认
upstream backend_pool {
server 192.168.1.11:8080;
server 192.168.1.12:8080;
server 192.168.1.13:8080;
}
最简单粗暴的算法,按顺序把请求分给每台服务器。适合后端服务器配置相同、无状态服务的场景。
2. 加权轮询(Weighted Round Robin)
upstream backend_pool {
server 192.168.1.11:8080 weight=5; # 处理50%的请求
server 192.168.1.12:8080 weight=3; # 处理30%的请求
server 192.168.1.13:8080 weight=2; # 处理20%的请求
}
服务器配置不同时使用。比如有台8核的机器和两台4核的,给8核的机器分配更多权重。
3. 最少连接数(Least Connections)
upstream backend_pool {
least_conn;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
server 192.168.1.13:8080;
}
把请求分给当前连接数最少的服务器。适合请求处理时间不均匀的场景,比如有些请求1秒就处理完,有些要10秒。
实战经验:我们的API网关一开始用轮询,后来发现有些慢接口会导致某台服务器连接堆积。换成least_conn后,负载变得均匀多了。
4. IP哈希(IP Hash)
upstream backend_pool {
ip_hash;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
server 192.168.1.13:8080;
}
根据客户端IP的哈希值分配服务器,同一个IP的请求总是落到同一台服务器。适合需要会话保持的场景。
注意:ip_hash有个坑,如果你前面还有一层代理(比如CDN),那所有请求的客户端IP都是CDN的IP,ip_hash就失效了。这时候需要用$http_x_forwarded_for来取真实IP。
5. 一致性哈希(Hash)
upstream backend_pool {
hash $request_uri consistent;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
server 192.168.1.13:8080;
}
根据指定的key进行哈希。consistent参数启用一致性哈希,当服务器增减时,只有少部分请求会重新映射。
常用的hash key:
$request_uri:相同URL总是落到同一台服务器,适合缓存场景
$arg_userid:按用户ID分流
$cookie_sessionid:按session分流
6. 随机(Random)
upstream backend_pool {
random two least_conn;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
server 192.168.1.13:8080;
}
随机选择两台服务器,然后从中选连接数少的那台。这个算法在Nginx 1.15.1引入,据官方说在大规模集群中效果比least_conn更好。
2.2.5 健康检查配置
负载均衡必须配合健康检查,不然一台服务器挂了,流量还往那儿发,用户体验就很差了。
被动健康检查(开源版Nginx):
upstream backend_pool {
server 192.168.1.11:8080 max_fails=3 fail_timeout=30s;
server 192.168.1.12:8080 max_fails=3 fail_timeout=30s;
server 192.168.1.13:8080 max_fails=3 fail_timeout=30s;
}
参数说明:
max_fails=3:连续3次请求失败就认为服务器挂了
fail_timeout=30s:服务器被标记为不可用的时间,30秒后会再次尝试
问题:被动检查有延迟,要等真实流量失败才能发现问题。而且恢复后也是立即全量切入,可能导致瞬间压力过大。
主动健康检查(Nginx Plus或第三方模块):
如果你用的是开源版Nginx,可以安装nginx_upstream_check_module模块:
upstream backend_pool {
server 192.168.1.11:8080;
server 192.168.1.12:8080;
server 192.168.1.13:8080;
# 主动健康检查配置
check interval=3000 rise=2 fall=3 timeout=2000 type=http;
check_http_send "GET /health HTTP/1.0
";
check_http_expect_alive http_2xx http_3xx;
}
server {
# 健康检查状态页面
location /upstream_status {
check_status;
access_log off;
allow 192.168.1.0/24;
deny all;
}
}
参数说明:
interval=3000:每3秒检查一次
rise=2:连续2次成功认为恢复
fall=3:连续3次失败认为宕机
type=http:使用HTTP检查
2.2.6 后备服务器配置
upstream backend_pool {
server 192.168.1.11:8080;
server 192.168.1.12:8080;
server 192.168.1.13:8080 backup; # 备用服务器
server 192.168.1.14:8080 down; # 标记为下线,不接收流量
}
backup:备用服务器,只有当所有主服务器都不可用时才启用
down:标记服务器为下线状态,通常用于维护
2.2.7 连接复用配置
每次代理请求都新建TCP连接太浪费了,配置keepalive可以复用连接:
upstream backend_pool {
server 192.168.1.11:8080;
server 192.168.1.12:8080;
server 192.168.1.13:8080;
# 每个worker保持的空闲连接数
keepalive 100;
# 单个连接最大请求数
keepalive_requests 1000;
# 连接空闲超时
keepalive_timeout 60s;
}
server {
location / {
proxy_pass http://backend_pool;
# 必须!使用HTTP/1.1和清空Connection头
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
这两行配置很容易漏掉:
proxy_http_version 1.1; proxy_set_header Connection "";
不加这两行,keepalive就不生效。因为HTTP/1.0默认是短连接,而且Nginx默认会把Connection头设成close。
2.3 启动和验证
2.3.1 配置验证
# 检查配置语法 nginx -t # 输出示例 # nginx: the configuration file /etc/nginx/nginx.conf syntax is ok # nginx: configuration file /etc/nginx/nginx.conf test is successful
2.3.2 重载配置
# 平滑重载,不中断服务 nginx -s reload # 或者使用systemd systemctl reload nginx
2.3.3 验证负载均衡
# 方法1:多次curl观察响应
for i in {1..10}; do
curl -s http://192.168.1.10 | grep "IP:"
sleep 0.5
done
# 方法2:使用ab压测
ab -n 1000 -c 100 http://192.168.1.10/
# 方法3:查看后端服务器访问日志
tail -f /var/log/nginx/access.log
三、示例代码和配置
3.1 完整配置示例
这是一个生产级的反向代理+负载均衡配置:
# /etc/nginx/nginx.conf
user nginx;
worker_processes auto;
worker_rlimit_nofile 65535;
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;
events {
worker_connections 65535;
use epoll;
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" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
keepalive_requests 10000;
# Gzip压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript;
# 上游服务器组
upstream api_servers {
least_conn;
server 192.168.1.11:8080 weight=3 max_fails=3 fail_timeout=30s;
server 192.168.1.12:8080 weight=3 max_fails=3 fail_timeout=30s;
server 192.168.1.13:8080 weight=2 max_fails=3 fail_timeout=30s;
server 192.168.1.14:8080 backup;
keepalive 300;
keepalive_requests 10000;
keepalive_timeout 60s;
}
# 静态资源服务器组
upstream static_servers {
server 192.168.1.21:80;
server 192.168.1.22:80;
keepalive 100;
}
# 限流配置
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/s;
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
# 主站配置
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name example.com www.example.com;
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
ssl_session_cache shared10m;
ssl_session_timeout 1d;
ssl_protocols TLSv1.2 TLSv1.3;
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# API接口
location /api/ {
limit_req zone=api_limit burst=50 nodelay;
limit_conn conn_limit 20;
proxy_pass http://api_servers;
proxy_http_version 1.1;
proxy_set_header Connection "";
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_connect_timeout 10s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 3;
proxy_next_upstream_timeout 30s;
}
# 静态资源
location /static/ {
proxy_pass http://static_servers;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_cache_valid 200 1d;
expires 30d;
add_header Cache-Control "public, immutable";
}
# WebSocket代理
location /ws/ {
proxy_pass http://api_servers;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400s;
}
# 健康检查接口
location /health {
access_log off;
return 200 'OK';
add_header Content-Type text/plain;
}
}
# 内部状态监控
server {
listen 127.0.0.1:8080;
location /nginx_status {
stub_status on;
access_log off;
}
}
}
3.2 实际应用案例
案例1:电商网站高可用架构
背景:日均PV 1000万,需要支持大促期间10倍流量峰值
架构设计:
┌─────────────────┐ │ CDN层 │ │ 静态资源加速 │ └────────┬────────┘ │ ┌────────▼────────┐ │ SLB/CLB层 │ │ 云负载均衡器 │ └────────┬────────┘ │ ┌───────────────────┼───────────────────┐ │ │ │ ┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐ │ Nginx 01 │ │ Nginx 02 │ │ Nginx 03 │ │ 反向代理集群 │ │ 反向代理集群 │ │ 反向代理集群 │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ └───────────────────┼───────────────────┘ │ ┌──────────────────────────┼──────────────────────────┐ │ │ │ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ API集群 │ │ Web集群 │ │ 静态资源 │ │ 商品/订单│ │ 前端渲染 │ │ 图片/CSS │ └─────────┘ └─────────┘ └─────────┘
Nginx配置:
# 商品服务
upstream product_service {
least_conn;
server 10.0.1.1:8080 weight=3;
server 10.0.1.2:8080 weight=3;
server 10.0.1.3:8080 weight=2;
keepalive 200;
}
# 订单服务
upstream order_service {
least_conn;
server 10.0.2.1:8080 weight=3;
server 10.0.2.2:8080 weight=3;
server 10.0.2.3:8080 weight=2;
keepalive 200;
}
# 用户服务
upstream user_service {
ip_hash; # 需要会话保持
server 10.0.3.1:8080;
server 10.0.3.2:8080;
server 10.0.3.3:8080;
keepalive 100;
}
server {
listen 80;
server_name api.shop.com;
# 商品接口
location /api/products {
proxy_pass http://product_service;
proxy_http_version 1.1;
proxy_set_header Connection "";
# ... 其他代理配置
}
# 订单接口
location /api/orders {
proxy_pass http://order_service;
proxy_http_version 1.1;
proxy_set_header Connection "";
# ... 其他代理配置
}
# 用户接口
location /api/users {
proxy_pass http://user_service;
proxy_http_version 1.1;
proxy_set_header Connection "";
# ... 其他代理配置
}
}
案例2:灰度发布配置
背景:新版本上线,先让10%的用户访问新版本,验证没问题后逐步扩大比例
# 新旧版本服务器
upstream app_v1 {
server 10.0.1.1:8080;
server 10.0.1.2:8080;
keepalive 100;
}
upstream app_v2 {
server 10.0.2.1:8080;
server 10.0.2.2:8080;
keepalive 100;
}
# 方法1:基于权重的灰度
upstream app_canary {
server 10.0.1.1:8080 weight=9; # v1
server 10.0.1.2:8080 weight=9; # v1
server 10.0.2.1:8080 weight=1; # v2 (10%)
server 10.0.2.2:8080 weight=1; # v2 (10%)
keepalive 100;
}
# 方法2:基于Cookie的灰度
map $cookie_version $upstream_group {
default app_v1;
"v2" app_v2;
}
# 方法3:基于请求头的灰度
map $http_x_version $upstream_group_header {
default app_v1;
"v2" app_v2;
}
# 方法4:基于用户ID的灰度(取模)
split_clients $arg_userid $app_version {
10% app_v2;
* app_v1;
}
server {
listen 80;
server_name app.example.com;
# 使用Cookie灰度
location / {
proxy_pass http://$upstream_group;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
# 设置灰度Cookie的接口
location /set_version {
add_header Set-Cookie "version=$arg_v; Path=/; Max-Age=86400";
return 200 "Version set to $arg_v";
}
}
灰度发布流程:
部署新版本服务到v2集群
配置10%流量到v2
监控v2的错误率、响应时间
逐步调整权重:10% -> 30% -> 50% -> 100%
下线v1集群
案例3:WebSocket负载均衡
背景:实时聊天应用,需要长连接支持
upstream ws_servers {
# WebSocket需要会话保持
ip_hash;
server 10.0.1.1:8080;
server 10.0.1.2:8080;
server 10.0.1.3:8080;
# WebSocket连接数较少,keepalive可以小一些
keepalive 50;
}
server {
listen 80;
server_name ws.example.com;
location / {
proxy_pass http://ws_servers;
proxy_http_version 1.1;
# WebSocket必须的头
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# WebSocket长连接超时设置
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
# 禁用缓冲
proxy_buffering off;
}
}
踩坑记录:WebSocket一开始配了least_conn,结果用户聊着聊着就断线了。排查发现是连接被分到了不同的服务器。WebSocket必须用ip_hash或者sticky session保证会话保持。
四、最佳实践和注意事项
4.1 最佳实践
1. 分层部署架构
不要把所有服务都放在一个upstream里,按业务功能分组:
# 好的做法
upstream user_api { ... }
upstream order_api { ... }
upstream product_api { ... }
# 不好的做法
upstream all_api {
server user1:8080;
server user2:8080;
server order1:8080; # 混在一起
server product1:8080; # 难以管理
}
2. 合理的超时设置
不同类型的请求设置不同的超时:
# 短请求(列表查询)
location /api/list {
proxy_read_timeout 10s;
}
# 长请求(报表导出)
location /api/export {
proxy_read_timeout 300s;
}
# 文件上传
location /api/upload {
client_max_body_size 500m;
proxy_read_timeout 600s;
}
3. 错误页面友好展示
# 自定义错误页面
error_page 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
internal;
}
# 或者返回JSON
error_page 502 503 504 = @fallback;
location @fallback {
default_type application/json;
return 503 '{"code": 503, "message": "Service temporarily unavailable"}';
}
4. 监控指标收集
日志中记录upstream响应时间,便于分析:
log_format upstream_log '$remote_addr [$time_local] ' '$request $status ' 'upstream: $upstream_addr ' 'response_time: $upstream_response_time ' 'connect_time: $upstream_connect_time';
5. 平滑扩缩容
新增服务器时,先设置低权重,逐步提升:
upstream backend {
server 10.0.1.1:8080 weight=10;
server 10.0.1.2:8080 weight=10;
server 10.0.1.3:8080 weight=1; # 新机器,先低权重
}
4.2 注意事项
| 常见错误 | 原因分析 | 解决方案 |
|---|---|---|
| 502 Bad Gateway | 后端服务未启动或连接被拒绝 | 检查后端服务状态,确认端口正确 |
| 504 Gateway Timeout | 后端响应超时 | 增加proxy_read_timeout,或优化后端性能 |
| upstream无法连接 | 网络不通或防火墙阻止 | 检查网络连通性,开放防火墙端口 |
| keepalive不生效 | 缺少必要配置 | 添加proxy_http_version 1.1和Connection "" |
| 负载不均衡 | 算法选择不当或权重配置问题 | 根据场景选择合适算法,检查权重配置 |
| 会话丢失 | 无状态轮询导致请求分发到不同服务器 | 使用ip_hash或sticky session |
| 健康检查延迟 | 被动检查需要真实流量触发 | 配置主动健康检查模块 |
| X-Forwarded-For链过长 | 多层代理重复追加 | 在入口处设置X-Forwarded-For为$remote_addr |
五、故障排查和监控
5.1 故障排查
5.1.1 常见故障诊断流程
502错误排查:
# 1. 检查后端服务是否运行 curl -I http://backend_ip:port/health # 2. 检查Nginx到后端的网络连通性 telnet backend_ip port # 3. 查看Nginx错误日志 tail -f /var/log/nginx/error.log # 4. 检查后端服务日志 # 看是否收到请求,是否有报错 # 5. 检查连接数是否达到限制 ss -s cat /proc/sys/net/core/somaxconn
504超时排查:
# 1. 测试后端接口响应时间 time curl http://backend_ip:port/api/slow # 2. 检查Nginx超时配置 nginx -T | grep -E "proxy_.*_timeout" # 3. 分析慢请求日志 awk '$NF > 5' /var/log/nginx/access.log # 响应时间>5秒的请求
负载不均排查:
# 1. 统计各后端服务器请求量
awk '{print $NF}' /var/log/nginx/access.log | sort | uniq -c | sort -rn
# 2. 检查upstream配置
nginx -T | grep -A 20 "upstream"
# 3. 检查是否有服务器被标记为不可用
# 查看error.log中的upstream相关错误
5.1.2 调试配置
临时开启详细日志排查问题:
# 临时配置,排查完记得关掉
error_log /var/log/nginx/error.log debug;
# 或者只对特定请求开启debug
location /api/problem {
error_log /var/log/nginx/debug.log debug;
proxy_pass http://backend;
}
5.2 性能监控
5.2.1 内置状态监控
server {
listen 127.0.0.1:8080;
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
deny all;
}
}
获取状态:
curl http://127.0.0.1:8080/nginx_status # 输出示例: # Active connections: 234 # server accepts handled requests # 12847932 12847932 98234756 # Reading: 5 Writing: 67 Waiting: 162
指标说明:
Active connections:当前活跃连接数
accepts:已接受的连接总数
handled:已处理的连接总数(应该等于accepts,不等说明有连接被丢弃)
requests:已处理的请求总数
Reading:正在读取请求头的连接数
Writing:正在发送响应的连接数
Waiting:Keep-alive空闲连接数
5.2.2 Prometheus监控集成
使用nginx-prometheus-exporter:
# 安装exporter docker run -d -p 9113:9113 nginx/nginx-prometheus-exporter:latest -nginx.scrape-uri=http://nginx:8080/nginx_status
Prometheus配置:
scrape_configs: - job_name: 'nginx' static_configs: - targets: ['localhost:9113']
关键告警规则:
groups:
- name: nginx_alerts
rules:
- alert: NginxHighConnectionUsage
expr: nginx_connections_active / nginx_connections_accepted > 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "Nginx连接使用率过高"
- 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错误率超过1%"
5.3 备份与恢复
5.3.1 配置备份
#!/bin/bash
# /usr/local/bin/backup_nginx.sh
BACKUP_DIR="/data/backup/nginx"
DATE=$(date +%Y%m%d_%H%M%S)
NGINX_DIR="/etc/nginx"
# 创建备份目录
mkdir -p ${BACKUP_DIR}
# 备份配置文件
tar -czf ${BACKUP_DIR}/nginx_config_${DATE}.tar.gz ${NGINX_DIR}
# 备份SSL证书
tar -czf ${BACKUP_DIR}/nginx_ssl_${DATE}.tar.gz ${NGINX_DIR}/ssl/
# 保留30天备份
find ${BACKUP_DIR} -name "*.tar.gz" -mtime +30 -delete
echo "Backup completed: ${BACKUP_DIR}/nginx_config_${DATE}.tar.gz"
5.3.2 配置恢复
#!/bin/bash # 恢复配置 BACKUP_FILE=$1 if [ -z "$BACKUP_FILE" ]; then echo "Usage: $0" exit 1 fi # 备份当前配置 mv /etc/nginx /etc/nginx.bak.$(date +%s) # 解压恢复 tar -xzf $BACKUP_FILE -C / # 测试配置 nginx -t if [ $? -eq 0 ]; then nginx -s reload echo "Configuration restored successfully" else echo "Configuration test failed, rolling back" rm -rf /etc/nginx mv /etc/nginx.bak.* /etc/nginx fi
六、总结
6.1 技术要点回顾
反向代理和负载均衡是Nginx最核心的功能,掌握好这两个技能,基本就能应对大部分Web架构需求了。
反向代理要点:
proxy_pass指令是核心
代理头设置影响后端获取真实信息
缓冲区配置影响内存使用和响应速度
超时配置要根据业务调整
负载均衡要点:
算法选择:无状态服务用least_conn,需要会话保持用ip_hash
健康检查:生产环境必须配置max_fails和fail_timeout
连接复用:keepalive可以显著提升性能
故障转移:proxy_next_upstream配置自动重试
6.2 进阶学习方向
Nginx Plus:商业版功能,主动健康检查、动态配置、更好的监控
OpenResty:整合Lua,实现复杂的业务逻辑
Envoy:云原生时代的新选择,功能更强大
服务网格:Istio、Linkerd等,下一代流量管理方案
6.3 参考资料
Nginx官方文档 - ngx_http_upstream_module: https://nginx.org/en/docs/http/ngx_http_upstream_module.html
Nginx官方文档 - ngx_http_proxy_module: https://nginx.org/en/docs/http/ngx_http_proxy_module.html
Nginx负载均衡指南: https://docs.nginx.com/nginx/admin-guide/load-balancer/
附录
A. 命令速查表
| 命令 | 说明 |
|---|---|
| nginx -t | 测试配置语法 |
| nginx -s reload | 平滑重载配置 |
| nginx -T | 输出完整配置 |
| curl -I http://url | 查看响应头 |
| ss -tlnp | 查看监听端口 |
| ss -ant | grep ESTAB | 查看已建立的连接 |
B. 配置参数详解
| 参数 | 上下文 | 默认值 | 说明 |
|---|---|---|---|
| proxy_pass | location | - | 代理目标地址 |
| proxy_connect_timeout | http,server,location | 60s | 连接超时 |
| proxy_send_timeout | http,server,location | 60s | 发送超时 |
| proxy_read_timeout | http,server,location | 60s | 读取超时 |
| proxy_next_upstream | http,server,location | error timeout | 触发重试的条件 |
| keepalive | upstream | - | 保持的空闲连接数 |
| weight | server | 1 | 服务器权重 |
| max_fails | server | 1 | 最大失败次数 |
| fail_timeout | server | 10s | 失败超时时间 |
C. 术语表
| 术语 | 解释 |
|---|---|
| 反向代理 | 代理服务端,客户端不知道真实服务器地址 |
| 正向代理 | 代理客户端,服务端不知道真实客户端地址 |
| 负载均衡 | 将请求分发到多台服务器 |
| Upstream | Nginx中定义后端服务器组的指令 |
| 健康检查 | 检测后端服务器是否可用 |
| 会话保持 | 同一用户的请求总是发到同一台服务器 |
| 灰度发布 | 逐步将流量切换到新版本 |
| 熔断 | 当后端不可用时自动停止转发请求 |
全部0条评论
快来发表一下你的评论吧 !