Nginx连接与请求速率限制主要由两组指令组成,它们是非常核心的流量控制和防攻击手段:
第一组指令:限制并发连接数
limit_conn_zone $binary_remote_addr zone=conn_per_ip:10m;
和limit_conn conn_per_ip 10;
指令 1:limit_conn_zone $binary_remote_addr zone=conn_per_ip:10m;
这行指令的作用是定义一个共享内存区域,用于存储每个 IP 地址的并发连接计数。
limit_conn_zone
:这是 Nginx 的指令,用于创建一个“连接限制”的共享内存区域。$binary_remote_addr
:这是一个 Nginx 内置变量,代表客户端的 IP 地址。这里使用$binary_remote_addr
而不是$remote_addr
是因为前者是二进制格式,占用空间更小(固定 4 字节 for IPv4,16 字节 for IPv6),而后者是字符串格式(如 “192.168.1.1”),长度在 7 到 15 字节之间,更节省内存。zone=conn_per_ip:10m:
zone=
关键字,后面跟区域定义。conn_per_ip
:这是你为这个共享内存区域起的名字,可以自定义。10m
:分配给这个区域的内存大小。10m 表示 10 兆字节(MB)。根据官方文档,在 64 位平台上,10m 可以存储大约 16 万个 IP 地址的状态信息。这个大小需要根据你的服务器情况和预期 IP 数量来调整。
简单来说:这一行创建了一个名为 conn_per_ip
、大小为 10MB 的“小黑板”,Nginx 会在这个小黑板上记录每个 IP ($binary_remote_addr
) 当前建立了多少个连接。
指令 2:limit_conn conn_per_ip 10;
这行指令的作用是在特定的 location 或 server 块中,应用上面定义的限制规则。
limit_conn
:这是应用连接限制的指令。conn_per_ip
:指定使用我们上面定义的、名为conn_per_ip
的共享内存区域。-
10
:每个 IP 地址同时允许的最大并发连接数。这里设置为 10。
工作流程:
当一个新连接到来时,Nginx 会:
查看 conn_per_ip
这个“小黑板”,找到该客户端 IP 当前的连接数。
如果连接数已经 >=10
,Nginx 会拒绝这个新连接(返回 503 Service Temporarily Unavailable
错误)。
如果连接数 <10
,则允许连接建立,并将该 IP 的连接计数加 1。
当这个连接关闭时,Nginx 会将计数减 1。
应用场景:
防止单个用户或恶意程序用大量并发连接拖垮服务器。例如,防止下载工具开过多线程,或者防止 CC 攻击建立大量空连接消耗服务器资源。
第二组指令:限制请求速率
这组指令同样分为两部分:定义共享内存区和速率 和 应用限制。
指令 1:limit_req_zone $binary_remote_addr zone=req_per_ip:10m rate=10r/s;
这行指令的作用是定义一个共享内存区域,用于存储每个 IP 地址的请求访问状态(像一个漏桶)。
limit_req_zone
:这是 Nginx 的指令,用于创建一个“请求限制”的共享内存区域。$binary_remote_addr
:同上,客户端的 IP 地址。zone=req_per_ip:10m
:req_per_ip
:为这个区域起的名字。10m
:内存大小,同样是 10MB。
rate=10r/s
:这是核心的速率限制参数。10r/s
表示每秒允许 10 个请求。r/s
是requests per second
。- 你也可以使用
r/m
(每分钟),例如rate=600r/m
同样表示每秒 10 个请求。
简单来说:这一行创建了一个名为 req_per_ip
、大小为 10MB 的“漏桶”,这个桶的漏水速率是每秒 10 个请求。
指令 2:limit_req zone=req_per_ip burst=20 nodelay;
这行指令的作用是在特定的 location 或 server 块中,应用上面定义的“漏桶”规则,并配置突发流量处理。
limit_req
:应用请求限制的指令。zone=req_per_ip
:指定使用名为 req_per_ip 的共享内存区域。burst=20
:突发容量。这相当于在“漏桶”上方加了一个缓冲区或队列。当请求的速率超过 rate 时,超出的请求不会立即被拒绝,而是先放入这个 burst 队列中排队等待处理。20 表示这个队列最多可以容纳 20 个请求。nodelay
:不延迟处理。这是一个非常重要的选项。- 如果没有
nodelay
:进入burst
队列的请求会被延迟处理,即以固定的rate
(每秒10个)的速度被处理,这会使请求的响应时间变长。 - 如果有
nodelay
:当请求到来时,只要burst
队列还有空位,Nginx 会立即处理这些请求,而不会延迟。但是,这并不意味着速率限制消失了。一旦burst
队列被填满,后续的请求会立即被拒绝。
- 如果没有
工作流程(以 burst=20 nodelay
为例):
- 第 1 秒内,来了 15 个请求。
- 前 10 个请求被正常处理(达到了
rate=10r/s
的速率)。 - 因为设置了
burst=20
,接下来的 5 个请求会被放入burst
队列,并被nodelay
立即处理。 - 此时,
burst
队列长度变为 5。
- 前 10 个请求被正常处理(达到了
- 第 2 秒内,来了 30 个请求。
- 前 10 个请求被正常处理(消耗了本秒的速率)。
- 接下来的 15 个请求会填满
burst
队列(因为队列还剩 20 - 5 = 15 个空位),并被nodelay
立即处理。 - 此时,
burst
队列已满(20/20)。 - 还剩下的 5 个请求 (30 - 10 - 15 = 5) 因为超过了 rate + burst 的总容量,Nginx 会立即返回 503 错误拒绝它们。
- 第 3 秒,没有新请求。Nginx 会从
burst
队列中“排出”10 个请求(因为速率是10r/s
),队列长度变为 10。
应用场景:
防止恶意刷接口、暴力破解密码、爬虫抓取等高频请求行为。同时,burst
和 nodelay
允许了正常的突发流量,避免了误伤那些在短时间内有合理高并发请求的正常用户(例如:页面加载时浏览器同时请求多个资源)。
总结与对比
特性 | 限制连接 (limit_conn ) |
限制请求 (limit_req ) |
---|---|---|
限制对象 | 并发连接数(同时存在的连接) | 请求速率(单位时间内的请求数) |
工作方式 | 计数,超过即拒绝 | 漏桶算法,平滑处理流量 |
核心指令 | limit_conn_zone , limit_conn |
limit_req_zone , limit_req |
内存区域 | zone=conn_per_ip:10m |
zone=req_per_ip:10m rate=10r/s |
应用参数 | limit_conn zone_name 10; (数字) |
limit_req zone_name burst=20 nodelay; |
应对场景 | 下载线程、CC空连接 | 刷API、暴力破解、爬虫 |
错误码 | 503 (Service Unavailable) | 503 (Service Unavailable) |
配置建议:
通常你会把 limit_conn_zone
和 limit_req_zone
放在 http {}
块中,使其全局生效。而 limit_conn
和 limit_req
则根据需要在 server {}
或 location {}
块中配置。对于登录、提交等关键接口,可以设置更严格的 limit_req
规则。