1. 为什么需要Nginx限流?
在互联网服务中,流量控制是一个永恒的话题。想象一下,你经营着一家网红餐厅,突然有1000个顾客同时涌入,而你的厨房只能同时处理50份订单。如果不采取任何措施,整个系统就会崩溃——服务器也是一样。
限流的核心价值主要体现在三个方面:
- 资源保护:防止单个IP或用户占用过多服务器资源(CPU、内存、连接数等)
- 服务稳定:确保在高并发场景下,关键业务仍能正常运行
- 安全防御:有效缓解CC攻击、爬虫滥用等恶意行为
提示:CC攻击(Challenge Collapsar)是一种通过大量合法请求耗尽服务器资源的攻击方式,与DDoS不同,它不需要庞大的僵尸网络,几个高性能服务器就能造成严重破坏。
Nginx作为最流行的Web服务器之一,其内置的limit_conn_zone模块就是专门为解决这类问题而生的。与第三方方案相比,它有三大优势:
- 原生支持,无需额外安装
- 基于共享内存,性能损耗极小
- 配置灵活,可针对不同维度限流
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. limit_conn_zone核心机制解析
2.1 模块工作原理
limit_conn_zone的工作流程可以类比为银行取号系统:
- 定义"等候区"大小(共享内存zone)
- 给每个客户发号码牌(基于$binary_remote_addr等key)
- 当等候区满时,新客户需要等待(返回503错误)
其核心配置指令只有两个:
nginx复制limit_conn_zone $binary_remote_addr zone=addr:10m;
limit_conn addr 10;
- 第一行:创建名为addr的共享内存区(10MB),以客户端IP为计数key
- 第二行:限制每个IP最多10个并发连接
注意:
$binary_remote_addr使用二进制格式存储IP,比$remote_addr节省更多空间(每个IPv4占4字节,IPv6占16字节)
2.2 内存占用计算
10MB内存能存储多少IP?我们可以做个简单计算:
- 每个IP计数器约占32/64字节(取决于系统)
- 额外开销约100字节/记录
- 按最大估算:10MB = 1010241024 ≈ 10,485,760字节
- 10,485,760 / (64+100) ≈ 64,000个独立IP
这意味着10MB内存区足以应对绝大多数场景。对于超大型网站,可以按需调整:
nginx复制# 应对百万级IP
limit_conn_zone $binary_remote_addr zone=massive:100m;
2.3 与相关模块对比
Nginx官方提供多个限流相关模块,各有侧重:
| 模块名称 | 作用维度 | 适用场景 | 典型配置示例 |
|---|---|---|---|
| limit_conn_zone | 并发连接数 | 防连接耗尽 | limit_conn addr 10; |
| limit_req_zone | 请求速率 | 防高频请求 | limit_req zone=one burst=5; |
| ngx_http_limit_conn_module | 总连接数 | 全局资源保护 | limit_conn_log_level error; |
实际防御CC攻击时,建议组合使用:
nginx复制limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
server {
limit_conn conn_limit 20;
limit_req zone=req_limit burst=20 nodelay;
}
3. 实战CC防御配置
3.1 基础防御配置
以下是一个完整的CC防护配置示例:
nginx复制http {
# 定义共享内存区
limit_conn_zone $binary_remote_addr zone=conn_zone:10m;
limit_req_zone $binary_remote_addr zone=req_zone:10m rate=5r/s;
# 异常IP黑名单(需配合定时更新)
geo $blocked_ip {
default 0;
include /etc/nginx/conf.d/blocked_ips.conf;
}
server {
# 连接数限制
limit_conn conn_zone 15;
# 请求速率限制(突发20个请求)
limit_req zone=req_zone burst=20 nodelay;
# 黑名单拦截
if ($blocked_ip) {
return 444;
}
location / {
# 静态资源不限制
limit_conn off;
limit_req off;
try_files $uri $uri/ /index.html;
}
location /api/ {
# API接口严格限制
limit_conn conn_zone 5;
limit_req zone=req_zone burst=10;
proxy_pass http://backend;
}
}
}
关键优化点:
- 分级防护:静态资源放开限制,动态API严格管控
- 黑名单联动:结合geo模块实现多层级防御
- burst参数:允许合理突发流量,避免误伤正常用户
3.2 高级防护技巧
3.2.1 动态限流值
通过map实现智能限流:
nginx复制map $http_user_agent $conn_limit {
default 15;
"~*Googlebot" 50;
"~*(curl|wget)" 3;
}
server {
limit_conn conn_zone $conn_limit;
}
3.2.2 分布式限流
对于多台Nginx组成的集群,需要统一计数。可以使用Redis+Lua方案:
nginx复制location /api/ {
access_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000) -- 1秒超时
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
ngx.log(ngx.ERR, "failed to connect to redis: ", err)
return
end
local key = "limit:" .. ngx.var.binary_remote_addr
local current = red:incr(key)
if current == 1 then
red:expire(key, 60) -- 60秒过期
end
if current > 100 then
ngx.exit(503)
end
}
}
3.2.3 日志分析
定制日志格式记录限流事件:
nginx复制log_format security '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'Conn:$connection_requests Req:$request_time '
'Limit:$limit_conn_status';
access_log /var/log/nginx/security.log security;
4. 性能调优与问题排查
4.1 压力测试
使用wrk进行限流效果测试:
bash复制# 测试100并发,持续30秒
wrk -t100 -c100 -d30s http://example.com/api/
# 带cookie模拟真实用户
wrk -t100 -c100 -d30s -H "Cookie: SESSION=test" http://example.com/
观察关键指标:
- 成功请求数(应保持在限制范围内)
- 503错误比例(突发流量时可能升高)
- 平均响应时间(不应因限流显著增加)
4.2 常见问题解决
4.2.1 误杀正常用户
现象:企业内网用户被集体限制
解决方案:
nginx复制# 信任内网IP段
geo $is_trusted {
default 0;
192.168.1.0/24 1;
10.0.0.0/8 1;
}
map $is_trusted $real_conn_limit {
1 100; # 内网宽松限制
0 $conn_limit;
}
4.2.2 内存占用过高
现象:Nginx内存持续增长
优化方案:
- 减小zone大小(测试找到最小值)
- 缩短key的过期时间(需配合定时任务清理)
- 使用更紧凑的key格式:
nginx复制limit_conn_zone $http_x_forwarded_for zone=proxy_zone:5m;
4.2.3 限流不生效
检查清单:
- 确认模块已加载:
nginx -V 2>&1 | grep -o limit_conn - 检查配置作用域(server/location)
- 验证共享内存是否耗尽:
bash复制# 查看内存使用
grep 'zone' /proc/$(cat /var/run/nginx.pid)/smaps
4.3 监控指标建议
建议监控以下关键指标:
| 指标名称 | 监控方式 | 告警阈值 |
|---|---|---|
| 限流触发次数 | Nginx日志分析 | >100次/分钟 |
| 共享内存使用率 | /proc/pid/smaps | >80% |
| 503错误比例 | 访问日志统计 | >5%持续5分钟 |
| 平均连接保持时间 | $upstream_response_time | >500ms |
Prometheus监控示例:
yaml复制- job_name: 'nginx'
metrics_path: '/status'
static_configs:
- targets: ['nginx:9113']
relabel_configs:
- source_labels: [__address__]
regex: '(.*):\d+'
target_label: 'instance'
5. 进阶应用场景
5.1 微服务API网关限流
在Kubernetes环境中,可以结合Ingress实现全局限流:
yaml复制apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-gateway
annotations:
nginx.ingress.kubernetes.io/limit-connections: "10"
nginx.ingress.kubernetes.io/limit-rps: "5"
spec:
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
5.2 动态限流策略
通过Lua脚本实现动态调整:
nginx复制location /admin/limit {
allow 192.168.1.100;
deny all;
content_by_lua_block {
local args = ngx.req.get_uri_args()
local redis = require "resty.redis"
local red = redis:new()
red:set("limit:rate", args.rate or "10r/s")
red:set("limit:conn", args.conn or "20")
ngx.say("Updated limit: ", args.conn, " conn, ", args.rate, " req/s")
}
}
5.3 移动端特殊处理
针对APP的限流优化:
nginx复制map $http_x_device_id $mobile_limit {
default $standard_limit;
"~*android|ios" 50;
}
server {
limit_conn mobile_zone $mobile_limit;
}
在实际生产环境中,我们曾遇到一个典型案例:某电商网站在大促期间,由于爬虫疯狂抓取商品详情页,导致正常用户无法访问。通过以下配置组合解决问题:
nginx复制# 基础防护
limit_conn_zone $binary_remote_addr zone=api_conn:12m;
limit_req_zone $binary_remote_addr zone=api_req:12m rate=20r/s;
# 爬虫特征识别
map $http_user_agent $is_bot {
default 0;
"~*(bot|crawl|spider|scraper)" 1;
}
# 分级限流
server {
location /product/ {
limit_conn api_conn 5;
limit_req zone=api_req burst=10 nodelay;
if ($is_bot) {
limit_req zone=api_req burst=2;
limit_conn api_conn 1;
}
}
}
这套配置实现了:
- 普通用户:每秒20请求,突发10个,最多5并发连接
- 识别出的爬虫:每秒20请求,突发2个,最多1并发连接
- 共享内存扩容到12MB,应对高并发场景
实施后,API服务器负载下降60%,而正常用户访问完全不受影响。这个案例充分展示了Nginx限流模块在实际业务中的价值——它不仅是技术工具,更是业务保障的重要手段。
