引言:
流量限制(rate-limiting)是nginx最有用的功能之一,却经常被错误理解和错误配置。它允许我们限制用户在给定时间内可以发出的HTTP请求数量。例如请求网站首页的GET请求,表单登录的POST请求等。
速率限制可以出于安全目的使用。例如,可以降低暴力破解账号密码的攻击速度。通过将传入请求速率限制为实际用户的典型值,并(通过记录)标识目标URL,它可以帮助我们防御DDOS攻击。而更为通用的用法是,防止上游应用程序服务器同时被太多用户请求所淹没。
一、nginx限流
Nginx官方版本限制IP的连接和并发分别有两个模块:
- limit_req_zone 用来限制单位时间内的请求数,即速率限制,采用的漏桶算法 "leaky bucket"。
- limit_req_conn 用来限制同一时间连接数,即并发限制。
二、限流算法
1.漏桶算法(限制请求次数)
漏桶算法思路很简单,请求先进入到漏桶里,漏桶以固定的速度出水,也就是处理请求,当水加的过快,则会直接溢出,也就是拒绝请求,可以看出漏桶算法能强行限制数据的传输速率。
是对于很多场景来说,除了要求能够限制数据的平均传输速率外,还要求允许某种程度的突发传输。这时候漏桶算法可能就不合适了,令牌桶算法更为适合。
2.令牌桶算法(限制请求速度)
对于很多应用场景来说,除了要求能够限制数据的平均传输速率外,还要求允许某种程度的突发传输。这时候漏桶算法可能就不合适了,令牌桶算法更为适合。
令牌桶算法的原理是系统以恒定的速率产生令牌,然后把令牌放到令牌桶中,令牌桶有一个容量,当令牌桶满了的时候,再向其中放令牌,那么多余的令牌会被丢弃;当想要处理一个请求的时候,需要从令牌桶中取出一个令牌,如果此时令牌桶中没有令牌,那么则拒绝该请求。
3.计数器(限制连接数)
计数器比较简单粗暴,比如我们限制的是1s能够通过的请求数,实现的思路就是从第一个请求进来开始计时,在接下来的1s内,每个请求进来请求数就+1,超过最大请求数的请求会被拒绝,等到1s结束后计数清零,重新开始计数。
这种方式有个很大的弊端:比如前10ms已经通过了最大的请求数,那么后面的990ms的请求只能拒绝,这种现象叫做“突刺现象”。
三、漏桶算法和令牌桶算法的区别
漏桶算法输入的时候请求不固定,但都会在漏桶里边先保存起来(小于漏桶的容量),然后输出的时候采用的是恒定的速率执行请求,有点像队列的先进先出,只是队列中的元素出队的时间间隔一致。
令牌桶算法跟漏桶算法刚好相反,令牌桶的大小就是接口所能承载的最大访问量,令牌的发放是恒速的,而最终能在某一时间处理的请求数不是恒定的,这取决于单位时间内令牌桶中的令牌数量。
从作用上来说,漏桶和令牌桶算法最明显的区别就是是否允许突发流量(burst)的处理,漏桶算法能够强行限制数据的实时传输(处理)速率,对突发流量不做额外处理;而令牌桶算法能够在限制数据的平均传输速率的同时允许某种程度的突发传输。
四、漏桶算法的基本配置
1.控制速率
在nginx.conf文件添加内容:
在http模块中添加
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
limit_req_zone:用来限制单位时间内的请求数
$binary_remote_addr:定义限流对象,binary_remote_addr 是一种key,表示基于 remote_addr(客户端IP) 来做限流,binary_ 的目的是压缩内存占用量。
zone=one:10m:定义共享内存区来存储访问信息, one:10m 表示一个大小为10M,名字为one的内存区域。1M能存储16000 IP地址的访问信息,10M可以存储16W IP地址访问信息。
rate=1r/s:用于设置最大访问速率,rate=10r/s 表示每秒最多处理10个请求。Nginx 实际上以毫秒为粒度来跟踪请求信息,因此 10r/s 实际上是限制:每100毫秒处理一个请求。这意味着,自上一个请求处理完后,若后续100毫秒内又有请求到达,将拒绝处理该请求。
配置 server模块,使用 limit_req 指令应用限流。
在location中添加
limit_req zone=one;
测试:
访问页面,在一秒内快速刷新
报错,F12查看报错信息,503报错
2.处理突发流量
现在超过1个请求就丢弃,没有“桶”的概念,所以我们需要添加一个“桶”
在 limit_req zone=one 后加上brust=8;
Burst:爆发,处理突发请求
burst参数定义了超出zone指定速率的情况下(示例中的mylimit区域,速率限制在每秒10个请求,或每100毫秒一个请求),客户端还能发起多少请求。上一个请求100毫秒内到达的请求将会被放入队列,我们将队列大小设置为8。
这意味着,如果从一个给定IP地址发送21个请求,Nginx会立即将第一个请求发送到上游服务器群,然后将余下20个请求放在队列中。然后每100毫秒转发一个排队的请求,只有当传入请求使队列中排队的请求数超过20时,Nginx才会向客户端返回503。
查看发现,我们访问了21个,一个请求立即进到上游服务器群,进入桶里8个,失败12个。
不过,单独使用 burst 参数并不实用。假设 burst=50 ,rate依然为10r/s,排队中的50个请求虽然每100ms会处理一个,但第50个请求却需要等待 50 * 100ms即 5s,这么长的处理时间自然难以接受。
因此,burst 往往结合 nodelay 一起使用。
3.配置 nodelay
表示我不要任何的延迟,只要burst桶一满就返回503 报错
配置完成后就没有等待的感觉
效果相当于每秒10个请求的“流量限制”。如果希望不限制两个请求间允许间隔的情况下实施“流量限制”,nodelay参数是很实用的。
报错日志可以去 /usr/local/nginx/logs/access.logs 里去查看
五、令牌桶算法配置
limit_req
令牌以每秒五个的速度往桶里放牌子;每个请求领一个牌子
一个请求可以领多个牌子,增加请求速率
限制下载速度
在配置中添加
limit_rate 1k :限制下载速度在1k左右
测试:
在html中添加压缩包
http://192.168.131.50/nginx-1.15.9.tar.gz
添加 limit_rate_after 1m 命令
就是在下载多少内容后再以limit_rate限制的速率下载
多用于浏览视屏
先给你一点先看,再慢慢给你缓存观看,等你欲罢不能的时候,让你冲会员,冲完会员就快 了!!!
重启,测试:
在html目录中添加一个大点的安装包
http://192.168.131.50/apache-jmeter-5.4.3.zip
先以5m的速度给你下载,再以1k左右下载
六、限制连接数
在nginx.conf添加内容
limit_conn_zone $binary_remote_addr zone=two:10m;
limit_conn_zone 1 表示限制单个IP同时最多能持有1个连接。
limit_conn two 1 表示虚拟主机(server) 同时能处理并发连接的总数。
需要注意的是:只有当 request header 被后端server处理后,这个连接才进行计数。
先注释其他限制,看下效果
全部访问成功了,看不出什么限制效果,我们添加限制速度
限制速度
发现失败了大部分,因为限制了并发数,他就会等待第一个请求处理完成后,才能处理第二个,而我们增加了限制处理速度,处理速度很慢,所以就失败了很多个