写在前面:Nginx 是我认为“性价比”最高的技术之一——学习成本低,但能解决的问题极多。本文不作教科书式的罗列,只分享实际生产环境中踩过坑、验证过的实战技巧。

来源:Nginx 能做什么好玩的事情? – 花宝宝的回答 – 知乎 https://www.zhihu.com/question/21483073/answer/1987553926904580027

1. 流量控制:用 map + split_clients 做真正的灰度发布

痛点:普通灰度发布仅调整 upstream 权重,导致同一用户刷新页面时版本跳变(一会儿新版,一会儿老版),体验极差。

核心思路:按用户 ID 的哈希值分流,保证同一用户始终访问同一版本。

配置示例

# split_clients 会对 cookie_user_id 做哈希,同一个 ID 永远落到同一个桶里
split_clients "${cookie_user_id}" $variant {
    10%    new;  # 10% 的用户走新版
    * old;  # 剩余走旧版
}

map $variant $backend {
    new    new_backend;
    old    old_backend;
}

upstream new_backend {
    server 192.168.1.11:8080;
}

upstream old_backend {
    server 192.168.1.10:8080;
}

server {
    location / {
        proxy_pass http://$backend;
    }
}

实战经验:

上线大改版时,建议路径:1% (观察三天) -> 10% -> 100% (全量)。

曾遇到新版页面白屏,直接将比例改为 0%,用户端无感知瞬间回滚。

2. 负载均衡详解:权重、容灾与健康检查

场景:后端有多台服务器,性能可能不一,且需要考虑单点故障的自动剔除。这里不讲最简单的轮询,讲点生产环境必须具备的策略。

配置示例

upstream backend_cluster {
    # 策略1:IP Hash
    # ip_hash; # 开启后同一 IP 固定访问同一台,解决 Session 丢失问题(已有 Redis Session 则不需要)

    # 策略2:权重分配 (性能强的多分担)
    server 192.168.1.10:8080 weight=5;
    server 192.168.1.11:8080 weight=1;

    # 策略3:容灾兜底 (Backup)
    # 正常情况不访问,只有当上面两台主服务器全挂了,才会请求这台
    # 常用于挂载"系统维护中"的静态页面服务
    server 192.168.1.12:8080 backup;
    
    # 策略4:停机维护 (Down)
    # 标记为 down,Nginx 会自动跳过,无需注释代码
    server 192.168.1.13:8080 down;

    # 策略5:被动健康检查 (关键)
    # 30秒内失败(超时/报错)达3次,则认为该节点挂掉,30秒内不再转发请求给它
    server 192.168.1.14:8080 max_fails=3 fail_timeout=30s;
}

server {
    location / {
        proxy_pass http://backend_cluster;
        # 必须加,否则后端拿不到真实 IP
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        
        # 配合 max_fails 使用,定义什么是"失败"(默认只有连接不上才算)
        # 加上 http_500 http_502,后端报错也算一次失败
        proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
    }
}

避坑指南:

  • ip_hash:如果你的 Nginx 前面还有一层负载均衡(如阿里云 SLB),且没有透传真实 IP,那么 ip_hash 可能会失效(所有请求被认为是同一个 IP)。
  • backup:这是个救命参数。当所有后端都挂了,backup 机器可以返回一个友好的“系统升级中”页面,而不是直接报错。

3. 限流避坑:彻底理解 burst 和 nodelay

痛点:配置了限流,但效果要么太严(误杀),要么没效果,核心是不理解这两个参数的组合逻辑。

基础配置

limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

location /api/ {
    # 关键配置
    limit_req zone=api burst=20 nodelay;
}

参数深度解析

  • rate=10r/s:令牌桶每秒放入 10 个令牌(即每 100ms 放入 1 个)。
  • burst=20:令牌桶容量,最多允许积压 20 个令牌(应对突发流量)。
  • nodelay:关键点。有令牌就立刻放行,不排队。

场景模拟

  1. 不加 nodelay:用户 1 秒内发 15 个请求。Nginx 会让这 15 个请求排队,每 100ms 处理一个。结果:用户感觉接口响应变慢,但不会报错。
  2. 加了 nodelay (推荐):桶里有 20 个令牌,瞬间来 15 个请求 -> 全部立刻放行,桶剩 5 个。若桶空了,第 21 个请求直接返回 503。

建议:

  • API 接口:建议加 nodelay,宁可限制报错,也别让用户排队空等。
  • 静态资源:可以不加,让请求排队慢慢加载,保护带宽。
  • 折中方案:limit_req zone=api burst=20 delay=10; (前 10 个立刻放行,后 10 个排队,兼顾体验与削峰)。

4. 性能优化:把 Nginx 当 CDN 用 (Proxy Cache)

场景:后端接口数据更新频率低(如热点文章列表),但请求量大,直接打数据库不仅浪费且易崩。

配置示例

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:100m inactive=60m max_size=1g;

server {
    location /api/hot-articles {
        proxy_cache api_cache;
        proxy_cache_valid 200 5m;           # 200 状态码缓存 5 分钟
        proxy_cache_valid 404 1m;           # 404 缓存 1 分钟
        proxy_cache_key "$request_uri";
        
        # 核心容灾配置:后端挂了或超时,直接返回旧缓存,保命神器
        proxy_cache_use_stale error timeout updating;
        
        # 调试用 header
        add_header X-Cache-Status $upstream_cache_status;
        
        proxy_pass http://backend;
    }
}

实战效果:某热点接口 QPS 从 2000 降至后端仅 400(命中率 80%),数据库压力骤降。

骚操作:配置 proxy_cache_bypass $cookie_nocache $arg_nocache;,运营人员访问时带上 ?nocache=1 即可强制刷新看最新效果。

5. 前端部署:try_files 的妙用与多项目共存

场景:Vue/React SPA 项目,刷新页面出现 404;或者一个域名下挂载多个前端项目。

标准 SPA 配置

location / {
    root /var/www/dist;
    index index.html;
    # 找不到文件就返回 index.html,交给前端路由处理
    try_files $uri $uri/ /index.html;
}

进阶:多项目共存 (root vs alias)

# 访问 /admin -> /var/www/admin-dist/index.html
location /admin {
    alias /var/www/admin-dist; # 注意这里用 alias
    try_files $uri $uri/ /admin/index.html;
}

# 访问 /h5 -> /var/www/h5-dist/index.html
location /h5 {
    alias /var/www/h5-dist;
    try_files $uri $uri/ /h5/index.html;
}

踩坑预警:

  • root + location /admin:实际寻找 /var/www/admin。
  • alias + location /admin:实际寻找 /var/www/admin-dist (替换逻辑)。
  • 这是 Nginx 新手最容易混淆的地方。

6. 日志分析:找出慢请求的真凶

痛点:默认日志看不出是网络慢、Nginx 慢还是后端慢。

方案:自定义 log_format,增加时间维度。

配置示例

log_format detailed '$remote_addr [$time_local] "$request" '
                    '$status $body_bytes_sent '
                    'rt=$request_time '                # 总时间
                    'uct=$upstream_connect_time '      # 连后端耗时
                    'uht=$upstream_header_time '       # 后端响应头耗时
                    'urt=$upstream_response_time '     # 后端完整响应耗时
                    '"$http_user_agent"';

判责指南

  • uht/urt 很大 (rt=5.2 uct=0.01 uht=5.1):后端背锅。处理逻辑慢,或者数据库慢。
  • uct 很大 (uct=5.0):连接层背锅。后端连接数满了,或者网络不通。
  • rt 很大,但 uct/uht 很小:Nginx 背锅。可能是 Gzip 压缩大文件耗时,或者客户端网速太慢(发送数据慢)。

7. 扩展能力:用 Nginx + Lua 做简易 API 网关

场景:不想上 Kong/APISIX 等重型网关,但需要做灵活的动态限流或鉴权。

方案:OpenResty 或 ngx_http_lua_module。

Redis 动态限流示例

location /api/ {
    access_by_lua_block {
        local limit = 100
        local key = ngx.var.remote_addr
        local redis = require "resty.redis"
        -- 连接 Redis 逻辑...
        
        local current = red:incr(key)
        if current == 1 then
            red:expire(key, 1)
        end
        
        if current > limit then
            ngx.exit(429)
        end
    }
    proxy_pass http://backend;
}

应用:比自带的 limit_req 灵活,可实现按 UserID 限流、黑名单拦截、简单签名验证。

8. 好玩实战:搭建私人直播服务器

工具:nginx-rtmp-module

流程:OBS 推流 -> Nginx 处理 -> 浏览器 HLS 播放。

核心配置

rtmp {
    server {
        listen 1935;
        application live {
            live on;
            hls on;
            hls_path /var/www/hls;
            hls_fragment 3; # 切片大小,影响延迟
        }
    }
}

http {
    server {
        location /hls {
            types {
                application/vnd.apple.mpegurl m3u8;
            }
            root /var/www;
            add_header Cache-Control no-cache;
        }
    }
}

最后修改日期: 2026年1月23日

作者

留言

撰写回覆或留言

发布留言必须填写的电子邮件地址不会公开。