lolly/docs/nginx/20-nginx-rate-limiting.md
xfy 972eab4267 refactor(docs): 重构文档目录结构,nginx 文档移至子目录
将 docs/ 根目录下的 nginx 相关文档统一移动到 docs/nginx/ 子目录,
提高文档组织性和可维护性。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 10:48:14 +08:00

893 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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;
# 高流量 APIburst = 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)