一,前言
限流可以用于安全目的上,比如减慢暴力密码破解攻击。通过限制进来的请求速率,并且(结合日志)标记出目标URLs来帮助防范DDoS攻击。一般地说,限流是用在保护上游应用服务器不被在同一时刻的大量用户请求湮没。
二,NGINX限流是如何工作的
NGINX 限流使用漏桶算法(leaky bucket algorithm)
,该算法广泛应用于通信和基于包交换计算机网络中,用来处理当带宽被限制时的突发情况。和一个从上面进水,从下面漏水的桶的原理很相似;如果进水的速率大于漏水的速率,这个桶就会发生溢出。
在请求处理过程中,水代表从客户端来的请求,而桶代表了一个队列,请求在该队列中依据先进先出(FIFO)
算法等待被处理。漏的水代表请求离开缓冲区并被服务器处理,溢出代表了请求被丢弃。
Nginx 提供两种限流方式,一是控制速率(访问频率),二是控制并发连接数。
三,限制访问频率(正常流量)
ngx_http_limit_req_module
模块提供限制请求处理速率能力,使用了漏桶算法(leaky bucket)
。下面例子使用 nginx limit_req_zone
和 limit_req
两个指令,限制单个IP的请求处理速率。
limit_req_zone
指令设置限流和共享内存区域的参数,但是该指令实际上并不限制请求速率。为了限制起作用,需要将该限制应用到某个特定的 location 或 server 块(block),通过包含一个 limit_req 指令的方式。
语法:limit_req_zone key zone rate
http {
limit_req_zone $binary_remote_addr zone=reqzone:10m rate=10r/s;
server {
location / {
imit_req zone=reqzone;
}
}
}
key
定义限流对象,binary_remote_addr 是一种key,表示基于 remote_addr(客户端IP) 来做限流,binary_ 的目的是压缩内存占用量。
Zone
定义了存储每个IP地址状态和它访问受限请求URL的频率的共享内存区域。将这些信息保存在共享内存中,意味着这些信息能够在 Nginx 工作进程之间共享。定义有两个部分:由 zone = 关键字标识的区域名称,以及冒号后面的区域大小。约 16000
个IP地址的状态信息消耗 1M 内存大小。
当 Nginx 需要添加新的记录时,如果此时存储耗尽了,最老的记录会被移除。如果释放的存储空间还是无法容纳新的记录,Nginx 返回503 (Service Temporarily Unavailable) 状态码。此外,为了防止内存被耗尽,每次 Nginx 创建一个新的记录的同时移除多达两条前60秒内没有被使用的记录。
rate
设置最大的请求速率。在上面的例子中,速率不能超过10个请求每秒。Nginx 事实上可以在毫秒级别追踪请求,因此这个限制对应了1个请求 每100毫秒。因为我们不允许突刺
(bursts,短时间内的突发流量,详细见下一部分),这意味着如果某个请求到达的时间离前一个被允许的请求小于100毫秒,它会被拒绝。
四,处理流量突刺(Bursts)
如果在100毫秒内得到2个请求会怎么样?对于第2个请求,NGINX返回503状态码给客户端。这可能不是我们想要的,因为事实上,应用是趋向于突发性的。相反,我们想要缓存任何过多的请求并且及时地服务它们。下面是我们使用 limit_req 的 burst
参数来更新配置:
http {
limit_req_zone $binary_remote_addr zone=reqzone:10m rate=10r/s;
server {
location / {
imit_req zone=reqzone burst=20;
}
}
}
burst 可以理解为桶的大小。 burst 参数定义了一个客户端能够产生超出区域(zone)规定的速率的请求数量(在我们示例 reqzone 区域中,速率限制是10个请求每秒,或1个请求每100毫秒)。一个请求在前一个请求后的100毫秒间隔内达到,该请求会被放入一个队列,并且该队列大小被设置为20.
这意味着如果从某个特定IP地址来的21个请求同时地达到,NGINX立即转发第一个请求到上游的服务器组,并且将剩余的20个请求放入队列中。然后,NGINX每100毫秒转发一个队列中的请求,并且只有当某个新进来的请求使得队列中的请求数目超过了20,则返回503给客户端。
五,无延迟排队
带有 burst 的配置产生平滑的网络流量,但是不实用,因为该配置会使得网站表现的很慢。在上面的例子中,队列中第20个数据包等待2秒才能被转发,这时该数据包的响应可能对于客户端已经没有了意义。为了处理这种情况,除了 burst 参数外,添加 nodelay
参数。
http {
limit_req_zone $binary_remote_addr zone=reqzone:10m rate=10r/s;
server {
location / {
imit_req zone=reqzone burst=20 nodelay;
}
}
}
nodelay 针对的是 burst 参数,burst=20 nodelay 表示这20个请求立马处理,不能延迟
,相当于特事特办。不过,即使这20个突发请求立马处理结束,后续来了请求也不会立马处理。burst=20 相当于缓存队列中占了20个坑,即使请求被处理了,这20个位置这只能按 100ms一个来释放。
这就达到了速率稳定,但突然流量也能正常处理的效果。
六,限制并发连接数
ngx_http_limit_conn_module
提供了限制连接数的能力,利用 limit_conn_zone
和 limit_conn
两个指令即可。下面是 Nginx 官方例子:
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
server {
...
limit_conn perip 10;
limit_conn perserver 100;
}
limit_conn perip 10 作用的 key 是 $binary_remote_addr
,表示限制单个IP同时最多能持有10个连接。
limit_conn perserver 100 作用的 key 是 $server_name
,表示虚拟主机同时能处理并发连接的总数。
需要注意的是:只有当 request header 被后端 server 处理后,这个连接才进行计数。
七,限制数据传输速度
location /flv/ {
flv;
limit_rate_after 20m;
limit_rate 100k;
}
这个限制是针对每个请求的,表示客户端下载前20M时不限速,后续限制100kb/s。
八,IP黑名单设置
①在conf目录下创建黑名单文件 black-ip.conf
,在文件内容写上列入黑名单的IP,格式为 deny IP
#拒绝访问IP
deny 192.168.1.1;
②在nginx.conf进行配置
http {
#黑名单
include black-ip.conf;
}
评论区