流量控制与限流
Nginx 通过连接数限制、请求速率限制实现流量控制,保护后端服务免受突发流量冲击。
连接数限制
limit_conn 模块
nginx
http {
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
server {
location / {
limit_conn perip 10; # 每 IP 10 个并发连接
limit_conn perserver 1000; # 总连接数 1000
limit_conn_status 503; # 超限返回状态码
limit_conn_log_level warn; # 超限日志级别
}
}
}
共享内存计算
| zone 大小 | 可存储 IP 数 |
|---|---|
| 1m | ~8,000 |
| 10m | ~80,000 |
| 100m | ~800,000 |
每个 IP 状态占用约 128 字节(32 位 IP)或 256 字节(128 位 IPv6)。
请求速率限制
limit_req 模块
nginx
http {
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
server {
location /api/ {
limit_req zone=api burst=20 nodelay;
}
}
}
算法参数
| 参数 | 说明 |
|---|---|
| rate | 平均请求速率(r/s 或 r/m) |
| burst | 突发队列大小 |
| nodelay | 立即处理突发请求,不排队 |
| delay | 延迟处理的阈值 |
漏桶算法
nginx
请求到达速率: 20r/s
配置速率: 10r/s
burst: 20
时间轴:
0s: 20 个请求到达
→ 10 个立即处理(符合 rate)
→ 10 个进入 burst 队列
→ 后续每秒处理 10 个 + 队列消费
无 nodelay: 排队等待
有 nodelay: 立即处理(占用 burst)
多级别限流
nginx
limit_req_zone $binary_remote_addr zone=ip_limit:10m rate=5r/s;
limit_req_zone $server_name zone=server_limit:10m rate=100r/s;
location /api/ {
limit_req zone=ip_limit burst=10 nodelay;
limit_req zone=server_limit burst=200;
}
突发流量处理
burst 与 delay
nginx
# 允许突发 20 个请求,立即处理
limit_req zone=api burst=20 nodelay;
# 允许突发 20 个请求,前 5 个立即,剩余排队
limit_req zone=api burst=20 delay=5;
限流状态码
lua
# 自定义限流响应
limit_req_status 429;
limit_req_log_level info;
# 自定义错误页
error_page 429 /429.json;
location = /429.json {
default_type application/json;
return 429 '{"error": "rate limit exceeded", "retry_after": 1}';
}
分布式限流
OpenResty + Redis
lua
-- redis_limit.lua
local redis = require("resty.redis")
local function check_rate_limit(key, limit, window)
local red = redis:new()
red:connect("127.0.0.1", 6379)
local count = red:incr(key)
if count == 1 then
red:expire(key, window)
end
if count > limit then
return false, count
end
return true, count
end
-- 使用
local ok, count = check_rate_limit("rate:" .. ngx.var.binary_remote_addr, 100, 60)
if not ok then
ngx.status = 429
ngx.say('{"error": "rate limit exceeded", "count": ' .. count .. '}')
return ngx.exit(429)
end
令牌桶算法
nginx
-- token_bucket.lua
local function token_bucket(key, capacity, refill_rate)
local redis = require("resty.redis")
local red = redis:new()
red:connect("127.0.0.1", 6379)
local now = ngx.time()
local data = red:hmget(key, "tokens", "last_refill")
local tokens = tonumber(data[1]) or capacity
local last_refill = tonumber(data[2]) or now
-- 补充令牌
local elapsed = now - last_refill
tokens = math.min(capacity, tokens + elapsed * refill_rate)
if tokens >= 1 then
tokens = tokens - 1
red:hmset(key, "tokens", tokens, "last_refill", now)
red:expire(key, 3600)
return true
end
return false
end
限流策略
API 分级限流
nginx
# 不同 API 不同限流
limit_req_zone $binary_remote_addr zone=read_api:10m rate=50r/s;
limit_req_zone $binary_remote_addr zone=write_api:10m rate=10r/s;
location /api/read/ {
limit_req zone=read_api burst=100 nodelay;
}
location /api/write/ {
limit_req zone=write_api burst=20 nodelay;
}
白名单配置
text
geo $white_list {
default 0;
10.0.0.0/8 1;
172.16.0.0/12 1;
192.168.0.0/16 1;
}
map $white_list $limit_key {
0 $binary_remote_addr;
1 ""; # 白名单不限流
}
limit_req_zone $limit_key zone=api:10m rate=10r/s;
要点总结
- limit_conn 限制并发连接数,limit_req 限制请求速率(漏桶算法)
- burst 控制突发队列,nodelay 立即处理,delay 延迟处理
- 共享内存 zone 大小决定可存储的 IP 状态数量(1m ≈ 8000 IP)
- OpenResty + Redis 实现分布式限流,支持跨 Nginx 节点统一限流
- 令牌桶算法比漏桶算法更灵活,支持突发流量平滑处理
- 通过 geo/map 模块配置白名单,避免内部服务被限流
📝 发现内容有误?点击此处直接编辑