Nginx反向代理和负载均衡配置实战

描述

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中定义后端服务器组的指令
健康检查 检测后端服务器是否可用
会话保持 同一用户的请求总是发到同一台服务器
灰度发布 逐步将流量切换到新版本
熔断 当后端不可用时自动停止转发请求

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分