# NGINX 限流与连接控制详解

## 1. ngx_http_limit_req_module (请求限流)

NGINX 的请求限流模块使用**令牌桶算法**实现，可以控制客户端的请求速率，保护后端服务器免受过载攻击。

### 1.1 limit_req_zone 定义限流区域

在 `http` 上下文中定义限流区域：

```nginx
http {
    # 基于 IP 地址的限流区域
    limit_req_zone $binary_remote_addr zone=ip_limit:10m rate=10r/s;

    # 基于服务器名称的限流区域
    limit_req_zone $server_name zone=server_limit:10m rate=100r/s;

    # 基于请求 URI 的限流区域
    limit_req_zone $uri zone=uri_limit:10m rate=5r/s;

    # 基于用户名的限流区域（需配合 auth_basic）
    limit_req_zone $remote_user zone=user_limit:10m rate=30r/m;
}
```

**指令参数说明**：

| 参数 | 说明 |
|------|------|
| `$binary_remote_addr` | 客户端二进制 IP 地址（节省内存） |
| `$remote_addr` | 客户端 IP 地址（文本格式） |
| `zone=name:size` | 共享内存区域名称和大小 |
| `rate=Nr/s` 或 `rate=Nr/m` | 限流速率（每秒/每分钟请求数） |
| `sync` | 多 worker 间同步限流状态（1.15.2+） |

**内存使用估算**：
- 1MB 共享内存约可存储 16,000 个 IP 地址状态（使用 `$binary_remote_addr`）
- 1MB 共享内存约可存储 8,000 个 IP 地址状态（使用 `$remote_addr`）

### 1.2 limit_req 应用限流

在 `server` 或 `location` 上下文中应用限流：

```nginx
server {
    location /api/ {
        limit_req zone=ip_limit burst=20 nodelay;
    }
}
```

**参数说明**：

| 参数 | 说明 | 默认值 |
|------|------|--------|
| `zone=name` | 使用的限流区域 | 必需 |
| `burst=N` | 突发请求数量（桶容量） | 0 |
| `nodelay` | 不延迟过量请求，立即处理 | - |
| `delay=N` | 开始延迟的阈值 | - |

### 1.3 limit_req_status 设置拒绝状态码

```nginx
server {
    location /api/ {
        limit_req zone=ip_limit burst=20;
        limit_req_status 429;  # 超过限流返回 429 Too Many Requests
    }
}
```

**常用状态码**：
- `503` - Service Unavailable（默认）
- `429` - Too Many Requests（推荐用于限流场景）

### 1.4 limit_req_log_level 设置日志级别

```nginx
server {
    location /api/ {
        limit_req zone=ip_limit burst=20;
        limit_req_log_level warn;  # 限流触发时使用 warn 级别记录
    }
}
```

**可选级别**：`info`, `notice`, `warn`, `error`

### 1.5 limit_req_dry_run 试运行模式

```
语法: limit_req_dry_run on | off;
默认: off
上下文: http, server, location
版本: 1.19.1+
```

**功能**: 试运行模式，只记录限流事件但不实际拒绝请求。用于测试限流配置效果。

```nginx
http {
    limit_req_zone $binary_remote_addr zone=prod:10m rate=100r/s;
    
    server {
        location /api/ {
            limit_req zone=prod burst=200 nodelay;
            limit_req_dry_run on;  # 试运行模式，不影响用户
            
            proxy_pass http://backend;
        }
    }
}
```

**应用场景**:
- 生产环境测试新限流策略
- 验证限流阈值是否合理
- 收集真实流量数据

### 1.6 $limit_req 变量

```
变量: $limit_req
功能: 存储请求因限流被延迟的时间（毫秒）
```

**用途**: 在日志中记录限流延迟，用于监控分析。

```nginx
log_format limit '$remote_addr - $limit_req ms - $request_uri';
access_log /var/log/nginx/limit.log limit;
```

**说明**: 如果请求未被限流延迟，变量值为空。

### 1.7 burst 和 nodelay 参数详解

**令牌桶算法原理**：

令牌桶算法包含两个核心概念：
- **速率 (rate)**：每秒向桶中放入的令牌数量
- **容量 (burst)**：桶中可容纳的最大令牌数

请求处理流程：
1. 每个请求需要消耗一个令牌
2. 如果桶中有令牌，请求立即处理
3. 如果桶中无令牌，请求被延迟或拒绝

**配置模式对比**：

**模式一：严格限流（无 burst）**
```nginx
limit_req zone=ip_limit;  # rate=1r/s
```
- 每秒只允许 1 个请求
- 多余请求立即返回 503

**模式二：允许突发（有 burst 无 nodelay）**
```nginx
limit_req zone=ip_limit burst=10;
```
- 允许突发 10 个请求
- 过量请求排队延迟处理
- 按 rate 速率逐渐释放

**模式三：立即处理突发（有 burst 有 nodelay）**
```nginx
limit_req zone=ip_limit burst=10 nodelay;
```
- 允许突发 10 个请求立即处理
- 超过 burst 的请求返回 503
- 最常用配置，用户体验最佳

**模式四：延迟阈值（使用 delay）**
```nginx
limit_req zone=ip_limit burst=20 delay=10;
```
- 前 10 个突发请求立即处理
- 第 11-20 个请求延迟处理
- 超过 20 个请求返回 503

### 1.8 多区域限流配置

**分层限流策略**：

```nginx
http {
    # 全局 IP 限流：每秒 10 请求
    limit_req_zone $binary_remote_addr zone=ip_global:10m rate=10r/s;

    # API 接口限流：每分钟 30 请求
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=30r/m;

    # 登录接口限流：每分钟 5 请求（更严格）
    limit_req_zone $binary_remote_addr zone=login_limit:10m rate=5r/m;

    server {
        listen 80;
        server_name api.example.com;

        # 全局限流
        limit_req zone=ip_global burst=20 nodelay;

        location /api/v1/ {
            # 叠加 API 限流
            limit_req zone=api_limit burst=5 nodelay;
            proxy_pass http://backend;
        }

        location /api/login {
            # 叠加登录限流（最严格）
            limit_req zone=login_limit burst=3 nodelay;
            proxy_pass http://backend;
        }
    }
}
```

**多区域组合规则**：
- 多个 `limit_req` 指令按顺序执行
- 任一区域超限即触发限流
- 可实现更精细的控制策略

### 1.9 动态试运行控制

```nginx
# 通过请求头控制试运行模式
map $http_x_dry_run $dry_run_mode {
    default off;
    test    on;
}

server {
    location /api/ {
        limit_req zone=api burst=200;
        limit_req_dry_run $dry_run_mode;
        proxy_pass http://backend;
    }
}
```

**说明**: `limit_req_dry_run` 支持使用变量动态控制，可根据请求头、Cookie 等条件灵活开启/关闭试运行模式。

---

## 2. ngx_http_limit_conn_module (连接限制)

连接限制模块控制并发连接数量，不同于请求限流（控制速率），它限制的是**同时打开的连接数**。

### 2.1 limit_conn_zone 定义连接限制区域

```nginx
http {
    # 基于 IP 的连接限制
    limit_conn_zone $binary_remote_addr zone=addr:10m;

    # 基于用户的连接限制
    limit_conn_zone $remote_user zone=user:10m;

    # 基于服务器的连接限制
    limit_conn_zone $server_name zone=server:10m;
}
```

**与 limit_req_zone 的区别**：
- 不需要 `rate` 参数（只计数，不限速）
- 统计的是并发连接数，不是请求速率

**sync 参数**（1.15.2+）：

在多 worker 环境下（worker_processes > 1），默认各 worker 独立计数。sync 参数使所有 worker 共享计数，实现精确的全局限流。

```nginx
limit_conn_zone $binary_remote_addr zone=addr:10m sync;
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s sync;
```

**适用场景**:
- 多 worker 部署需要精确统计
- 分布式限流场景
- 高并发环境防止计数偏差

### 2.2 limit_conn 应用连接限制

```nginx
server {
    location /download/ {
        limit_conn addr 10;  # 每个 IP 最多 10 个并发连接
    }
}
```

**常见应用场景**：

**限制下载并发**：
```nginx
location /download/ {
    limit_conn addr 5;           # 每 IP 最多 5 个并发下载
    limit_rate_after 10m;        # 前 10MB 不限速
    limit_rate 100k;             # 之后限速 100KB/s
}
```

**限制整体连接**：
```nginx
server {
    limit_conn server 1000;      # 整个服务器最多 1000 个并发连接

    location / {
        proxy_pass http://backend;
    }
}
```

### 2.3 limit_conn_dry_run 试运行模式

```
语法: limit_conn_dry_run on | off;
默认: off
上下文: http, server, location
版本: 1.19.1+
```

**功能**: 连接限制试运行模式。

```nginx
http {
    limit_conn_zone $binary_remote_addr zone=addr:10m;
    
    server {
        location /download/ {
            limit_conn addr 10;
            limit_conn_dry_run on;
            proxy_pass http://backend;
        }
    }
}
```

### 2.4 limit_conn_status 设置拒绝状态码

```nginx
server {
    location /download/ {
        limit_conn addr 10;
        limit_conn_status 503;   # 超过连接限制返回 503
    }
}
```

### 2.5 limit_conn_log_level 设置日志级别

```nginx
server {
    location /download/ {
        limit_conn addr 10;
        limit_conn_log_level warn;
    }
}
```

---

## 3. ngx_stream_limit_conn_module (Stream 连接限制)

Stream 模块用于 TCP/UDP 四层代理的连接限制。

### 3.1 Stream 上下文中的连接限制

```nginx
stream {
    # 定义限流区域
    limit_conn_zone $binary_remote_addr zone=stream_addr:10m;

    server {
        listen 3306;
        proxy_pass db_backend;
        limit_conn stream_addr 5;  # 每 IP 最多 5 个并发连接
    }
}
```

### 3.2 与 HTTP 连接限制的区别

| 特性 | HTTP (ngx_http_limit_conn_module) | Stream (ngx_stream_limit_conn_module) |
|------|-----------------------------------|---------------------------------------|
| 上下文 | http, server, location | stream, server |
| 适用协议 | HTTP/HTTPS | TCP/UDP |
| 变量支持 | 完整 | 有限（主要使用 `$binary_remote_addr`） |
| 连接统计 | 基于请求 | 基于连接 |

### 3.3 Stream 综合配置示例

```nginx
stream {
    # 连接限制区域
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;

    # 日志格式
    log_format stream_log '$remote_addr [$time_local] $protocol '
                          '$bytes_sent $bytes_received $session_time';

    access_log /var/log/nginx/stream-access.log stream_log;

    upstream mysql_backend {
        server 192.168.1.10:3306;
        server 192.168.1.11:3306 backup;
    }

    server {
        listen 3306;
        proxy_pass mysql_backend;

        # 连接限制
        limit_conn conn_limit 10;
        limit_conn_status 503;

        # 超时设置
        proxy_connect_timeout 5s;
        proxy_timeout 300s;
    }

    server {
        listen 6379;
        proxy_pass redis_backend;

        # 更严格的连接限制
        limit_conn conn_limit 20;
    }
}
```

---

## 4. 实际应用场景

### 4.1 API 限流保护

```nginx
http {
    # API 限流区域：每秒 100 请求
    limit_req_zone $binary_remote_addr zone=api_limit:50m rate=100r/s;

    # 按 API Key 限流（更精确）
    limit_req_zone $http_x_api_key zone=api_key_limit:100m rate=1000r/s;

    server {
        listen 443 ssl;
        server_name api.example.com;

        location /v1/ {
            # 双重限流：IP + API Key
            limit_req zone=api_limit burst=200 nodelay;
            limit_req zone=api_key_limit burst=500 nodelay;

            limit_req_status 429;
            limit_req_log_level warn;

            proxy_pass http://api_backend;
        }
    }
}
```

### 4.2 登录接口防护

防止暴力破解和撞库攻击：

```nginx
http {
    # 登录接口：每分钟 5 次尝试
    limit_req_zone $binary_remote_addr zone=login_limit:10m rate=5r/m;

    # 基于用户名限流（防止针对特定用户的攻击）
    limit_req_zone $arg_username zone=user_login_limit:10m rate=10r/m;

    server {
        listen 443 ssl;
        server_name auth.example.com;

        location /login {
            # 严格限流
            limit_req zone=login_limit burst=3 nodelay;
            limit_req zone=user_login_limit burst=5 nodelay;

            limit_req_status 429;

            proxy_pass http://auth_backend;
        }

        # 密码重置同样限流
        location /forgot-password {
            limit_req zone=login_limit burst=3 nodelay;
            proxy_pass http://auth_backend;
        }
    }
}
```

### 4.3 DDoS 防护基础

多层防护策略：

```nginx
http {
    # 第一层：IP 级请求限流
    limit_req_zone $binary_remote_addr zone=ip_req:100m rate=50r/s;

    # 第二层：IP 级连接限制
    limit_conn_zone $binary_remote_addr zone=ip_conn:100m;

    # 第三层：URI 级限流（防止针对特定接口的攻击）
    limit_req_zone $binary_remote_addr$uri zone=uri_req:100m rate=10r/s;

    server {
        listen 80;
        server_name example.com;

        # 全局防护
        limit_req zone=ip_req burst=100 nodelay;
        limit_conn ip_conn 50;

        # 静态资源放宽限制
        location ~* \.(jpg|jpeg|png|gif|css|js)$ {
            limit_req off;
            limit_conn off;
            expires 30d;
        }

        # 搜索接口严格限流
        location /search {
            limit_req zone=ip_req burst=20 nodelay;
            limit_req zone=uri_req burst=5 nodelay;
            proxy_pass http://backend;
        }

        # 高危接口极严格限流
        location /admin/ {
            limit_req zone=ip_req burst=5 nodelay;
            limit_req zone=uri_req burst=2 nodelay;
            limit_conn ip_conn 5;

            # 只允许特定 IP
            allow 10.0.0.0/24;
            deny all;
        }
    }
}
```

### 4.4 动态限流（使用变量）

根据请求特征动态限流：

```nginx
http {
    # 根据请求方法设置不同限流键
    map $request_method $rate_limit_key {
        default $binary_remote_addr;
        POST    $binary_remote_addr:POST;
        GET     $binary_remote_addr:GET;
    }

    # 基于 User-Agent 的限流
    map $http_user_agent $ua_limit_key {
        default $binary_remote_addr;
        ~*bot   $binary_remote_addr:BOT;
        ~*curl  $binary_remote_addr:CURL;
    }

    # 动态限流区域
    limit_req_zone $rate_limit_key zone=method_limit:10m rate=50r/s;
    limit_req_zone $ua_limit_key zone=ua_limit:10m rate=30r/s;

    server {
        location / {
            # 应用动态限流
            limit_req zone=method_limit burst=100 nodelay;
            limit_req zone=ua_limit burst=50 nodelay;

            proxy_pass http://backend;
        }
    }
}
```

### 4.5 白名单配置

使用 `geo` 模块实现白名单：

```nginx
http {
    # 定义白名单
    geo $limit_key {
        default $binary_remote_addr;

        # 白名单 IP 返回空值（不限流）
        10.0.0.0/24 "";
        192.168.1.0/24 "";
        127.0.0.1 "";
    }

    # 使用白名单的限流区域
    limit_req_zone $limit_key zone=white_limit:10m rate=10r/s;
    limit_conn_zone $limit_key zone=white_conn:10m;

    server {
        location / {
            # 白名单 IP 不会触发限流
            limit_req zone=white_limit burst=20 nodelay;
            limit_conn white_conn 10;

            proxy_pass http://backend;
        }
    }
}
```

**高级白名单配置**（使用 map）：

```nginx
http {
    geo $white_ip {
        default 0;
        10.0.0.0/24 1;
        192.168.1.0/24 1;
        127.0.0.1 1;
    }

    map $white_ip $limit_key {
        0 $binary_remote_addr;      # 非白名单：使用 IP
        1 "";                        # 白名单：空值（不限流）
    }

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

    server {
        location /api/ {
            limit_req zone=api burst=20 nodelay;
            proxy_pass http://backend;
        }
    }
}
```

---

## 5. 性能影响和最佳实践

### 5.1 性能影响分析

**内存使用**：

| 配置项 | 内存估算 |
|--------|----------|
| `limit_req_zone` 10MB | 约 160,000 个 IP 状态 |
| `limit_conn_zone` 10MB | 约 160,000 个连接状态 |
| 每个状态项 | 约 64-128 字节 |

**CPU 开销**：
- 限流检查：O(1) 时间复杂度
- 对高并发场景影响极小（通常 < 1% CPU）

**延迟影响**：
- `nodelay` 模式：无额外延迟
- 排队模式：可能增加请求延迟

### 5.2 配置最佳实践

**1. 合理设置 zone 大小**
```nginx
# 根据预期并发数计算
# 10,000 并发 IP × 64 字节 ≈ 640KB
# 留 2-3 倍余量：建议 2-5MB
limit_req_zone $binary_remote_addr zone=api:5m rate=100r/s;
```

**2. 使用二进制格式变量**
```nginx
# 推荐：节省 50% 内存
limit_req_zone $binary_remote_addr zone=good:10m rate=10r/s;

# 不推荐：浪费内存
limit_req_zone $remote_addr zone=bad:10m rate=10r/s;
```

**3. 分层限流策略**
```nginx
http {
    # 全局宽松限流
    limit_req_zone $binary_remote_addr zone=global:50m rate=100r/s;

    # API 严格限流
    limit_req_zone $binary_remote_addr zone=api:20m rate=10r/s;

    server {
        # 全局限流
        limit_req zone=global burst=200 nodelay;

        location /api/ {
            # 叠加 API 限流
            limit_req zone=api burst=20 nodelay;
        }
    }
}
```

**4. 静态资源排除**
```nginx
location ~* \.(css|js|png|jpg|jpeg|gif|ico|woff|woff2)$ {
    limit_req off;
    limit_conn off;
    expires 30d;
}
```

**5. 合理设置 burst**
```nginx
# 推荐：burst = rate × 2（允许 2 秒突发）
limit_req zone=api rate=10r/s burst=20 nodelay;

# 高流量 API：burst = rate × 5
limit_req zone=high_api rate=100r/s burst=500 nodelay;
```

### 5.3 监控与调试

**查看限流状态**：
```bash
# 查看限流触发日志
tail -f /var/log/nginx/error.log | grep "limiting requests"

# 统计限流次数
awk '/limiting requests/ {print $1}' /var/log/nginx/error.log | sort | uniq -c
```

**添加限流监控日志**：
```nginx
log_format limit_log '$remote_addr - $time_local '
                     'req_zone=$limit_req_zone conn_zone=$limit_conn_zone '
                     'status=$status';

access_log /var/log/nginx/limit.log limit_log;
```

**NGINX Plus 状态监控**（商业版）：
```nginx
server {
    location /api/ {
        limit_req zone=api burst=20 nodelay;

        # 暴露限流统计
        location /api/status {
            api /status/http/limit_reqs;
        }
    }
}
```

### 5.4 常见问题排查

**问题一：限流不生效**
```nginx
# 检查 zone 是否正确定义
# 确保在 http 上下文定义 zone
http {
    limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;  # ✓ 正确
}

# 错误示例：在 server 中定义
server {
    limit_req_zone ...  # ✗ 错误，必须在 http 上下文
}
```

**问题二：内存溢出**
```nginx
# 错误：zone 太小
limit_req_zone $binary_remote_addr zone=small:1m rate=100r/s;
# 1MB 只能存储约 16,000 个状态
# 高并发时会被覆盖，限流失效

# 正确：根据流量预估
limit_req_zone $binary_remote_addr zone=proper:50m rate=100r/s;
```

**问题三：误杀正常用户**
```nginx
# 错误配置：burst 过小，无 nodelay
limit_req zone=api burst=2;  # 请求会排队，用户体验差

# 正确配置：合理的 burst + nodelay
limit_req zone=api burst=50 nodelay;  # 允许突发，立即处理
```

### 5.5 综合配置模板

```nginx
user nginx;
worker_processes auto;

http {
    # ========== 限流区域定义 ==========

    # 全局 IP 限流：每秒 50 请求
    limit_req_zone $binary_remote_addr zone=ip_global:50m rate=50r/s;

    # API 限流：每秒 10 请求
    limit_req_zone $binary_remote_addr zone=api_limit:20m rate=10r/s;

    # 登录限流：每分钟 10 请求
    limit_req_zone $binary_remote_addr zone=login_limit:10m rate=10r/m;

    # 连接限制：每 IP 最多 50 连接
    limit_conn_zone $binary_remote_addr zone=conn_limit:50m;

    # ========== 白名单 ==========

    geo $white_ip {
        default 0;
        10.0.0.0/24 1;
        192.168.0.0/16 1;
        127.0.0.1 1;
    }

    map $white_ip $limit_key {
        0 $binary_remote_addr;
        1 "";
    }

    # ========== 服务器配置 ==========

    server {
        listen 80;
        server_name example.com;

        # 全局限流（白名单除外）
        limit_req zone=ip_global burst=100 nodelay;
        limit_conn conn_limit 50;

        limit_req_status 429;
        limit_conn_status 503;
        limit_req_log_level warn;

        # 静态资源不限流
        location ~* \.(css|js|png|jpg|jpeg|gif|ico|woff|woff2|ttf)$ {
            limit_req off;
            limit_conn off;
            expires 30d;
            root /var/www/static;
        }

        # API 接口
        location /api/ {
            limit_req zone=api_limit burst=20 nodelay;
            proxy_pass http://api_backend;
        }

        # 登录接口
        location /login {
            limit_req zone=login_limit burst=5 nodelay;
            proxy_pass http://auth_backend;
        }

        # 默认路由
        location / {
            proxy_pass http://backend;
        }
    }
}

# ========== Stream 连接限制 ==========

stream {
    limit_conn_zone $binary_remote_addr zone=stream_conn:10m;

    server {
        listen 3306;
        proxy_pass mysql_backend;
        limit_conn stream_conn 10;
    }
}
```

---

## 6. 参考文档

- [NGINX Limiting Requests](http://nginx.org/en/docs/http/ngx_http_limit_req_module.html)
- [NGINX Limiting Connections](http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html)
- [NGINX Stream Limit Connections](http://nginx.org/en/docs/stream/ngx_stream_limit_conn_module.html)
