一、前言
1.1什么是限流?
限流存在于高可用服务中。
用于高可用的保护手段,主要包括:缓存,降级,限流
限流:只允许指定的事件进入系统,超过的部分将被拒绝服务,排队或者降级处理。
1.2为什么需要限流?
一:服务扛不住压力了
二:因为资源的稀缺或者处于安全防范的目的而采用的自我保护机制,保证有限的资源提供最大的服务能力,按照预期的流量提供服务 ,超过的部分将会拒绝服务,排队或者降级处理
二、按照限流算法分类
2.1.计数器(固定窗口计数器)
通用及最简单的算法,简单逻辑是维护一个固定时间的计数器,如果检测到单位时间过去则记为0,重新计数
存在的问题
1.没有应对突发的流量的能力:如100的qps,前100ms来了99个,后900ms只能处理一个
2.不准确,在一个窗口内后半段时间来了100个请求,以及在后一个窗口的前半段时间100个请求,等同于在一个窗口内出现了双倍的限流的流量!
2.2 漏铜算法
水:请求
桶:限流阈值大小
漏的水滴:固定速率,服务的处理速度
实现步骤:
1.将每个请求放入固定的大小的队列进行存储
2.以固定的速率向外流出请求,队列为空则停止流出
3.队列满了则拒绝服务
2.3令牌桶算法
为了保证网站在遭受流量攻击的时候,仍然能够保障正常用户的访问不受到影响,有必要对网站添加限流措施。通过简单的配置Nginx,可以帮助我们实现这一功能。
优点:令牌桶支持突发流量
因为桶中有多少令牌在等待,就允许有多少突发请求可以执行
实现步骤:
1.以恒定的速率向桶中增加令牌
2.如果令牌满了直接丢弃,如果有请求则获取令牌进行请求
3.如果桶空了,则拒绝执行
三、Nginx实现服务限流
实现服务限流的方式有很多种,下面我主要介绍的是Nginx实现,优点是不用频繁部署jar服务,减少代码编写。提高效率,短时间解决问题
Nginx支持通过以下维度,来对用户的请求进行限制:
- 控制访问频率
- 控制并发连接数
- 控制访问速率
- IP黑白名单
3.1超过访问频率就立即拒绝
对应Nginx模块:ngx_http_limit_req_module
# 限流设置,这只是设置流量限制和共享内存区域的参数,但实际上并不限制请求速率。具体的限制需要定义具体的url
limit_req_zone $binary_remote_addr zone=contentRateLimit:10m rate=10r/s;
# 指定错误码
limit_req_status 429;
server {
location /update_content {
proxy_pass http://192.168.211.1:18081;
}
location /read_content {
# 使用限流配置
limit_req zone=contentRateLimit;
proxy_pass http://192.168.211.1:18081;
}
}
- binary_remote_addr:表示基于客户端IP做限流,使用二进制来表示IP地址;
- zone:定义共享内存区来存储访问信息;
- contentRateLimit:10m 表示一个大小为10M,名字为contentRateLimit的内存区域; 1M能存储8000个IP地址的访问信息(64位平台);
- rate:用于设置最大访问速率,rate=10r/s 表示每秒最多处理10个请求。Nginx 实际上以毫秒为粒度来跟踪请求信息,因此 10r/s 实际上是限制:每100毫秒处理一个请求;
- limit_req_zone:只定义速度限制的相关参数,一般用于http块中,使其可以在多个相关server中使用;
- limit_req:启用定义的限速参数;
当存储空间耗尽的时候,如果需要记录新的值,那么就会通过LRU算法移除旧的变量来腾出空间,如果这样腾出来的空间还是不足以接纳新的记录值,那么nginx就会返回状态码503。此外,为了防止内存耗尽,nginx每次创建一个新记录值的时候就会清理掉两个60秒内没被使用过的旧记录值。
(2) 支持处理突发流量
超过流量不会立即被拒绝,允许加入一个队列,只有队列满了之后的请求才会被拒绝。如果有时正常流量突然增大,超出的请求将被拒绝,无法处理突发流量,就可以结合 burst 参数使用来解决该问题。
3.2支持处理突发流量
超过流量不会立即被拒绝,允许加入一个队列,只有队列满了之后的请求才会被拒绝。如果有时正常流量突然增大,超出的请求将被拒绝,无法处理突发流量,就可以结合 burst 参数使用来解决该问题。
# 限流设置,这只是设置流量限制和共享内存区域的参数,但实际上并不限制请求速率。具体的限制需要定义具体的url
limit_req_zone $binary_remote_addr zone=contentRateLimit:10m rate=10r/s;
server {
location /update_content {
proxy_pass http://192.168.211.1:18081;
}
location /read_content {
# 使用限流配置
limit_req zone=contentRateLimit burst=4 nodelay;
proxy_pass http://192.168.211.1:18081;
}
}
- burst :表示在超过设定的处理速率后能额外处理的请求数。burst=4表示有4个请求到达,Nginx
会处理第一个请求,剩余3个请求将放入队列,然后每隔100ms从队列中获取一个请求进行处理。若请求数大于4,将拒绝处理多余的请求,直接返回503。排队中的请求虽然每100ms会处理一个,但却需要等待
较长的处理时间。所以burst 往往结合 nodelay 一起使用,以实现削峰填谷的效果; - nodelay :假设我们的流量是 2,1,4,0,2
,正常的请求会存在峰值和谷值,代表每秒的请求数。这样当流控为2r/s,burst=4
nodelay时,在第3秒请求数为4时(峰值),仍然允许直接处理4个请求,但是后续的请求会被拦截,保证总流量不超过2r/s,因此,当第四秒请求数为0时,就起到了削峰填谷的作用。假设流量是2,1,4,4,4
,峰值持续的时间比较长,那么从第二个峰值开始,就会被真的流控,被拒绝或进行排队,这样即使被处理,也会延迟稍高!
3.3控制并发连接数
Nginx内置的limit_conn_zone和limit_conn指令,提供了通过限制ip连接数来控制流量的能力。
其中只有当服务器正在处理请求并且已经读取了整个请求头时,才会计算为有效连接。
对应Nginx模块:ngx_http_limit_conn_module
(1) 控制每个IP的连接数
limit_conn_zone $binary_remote_addr zone=perip:10m;
server {
location /brand {
# 同一个地址只允许连接2次
limit_conn perip 2;
proxy_pass http://192.168.211.1:18081;
}
}
limit_conn_zone $binary_remote_addr zone=perip:10m :表示根据用户的IP地址来进行限制,设置共享内存大小为10M。
limit_conn perip 2 :表示同一个地址只允许连接2次。
(2) 控制与服务器的连接总数
# 根据server_name来限制,存储内存大小10M
limit_conn_zone $server_name zone=perserver:10m;
server {
listen 80;
server_name xx.com;
location / {
# 限制与服务器的总连接数
limit_conn perserver 100;
root html;
index index.html index.htm;
}
}
limit_conn_zone $server_name zone=perserver:10m :表示根据用户访问的server_name来进行限制,设置共享内存大小为10M。
limit_conn perserver 100 :表示同一个server只允许连接100次。
3.4控制访问速率
设置http请求传输多少字节后开始限速。
对应Nginx模块:ngx_http_core_module
location /flv/ {
limit_rate_after 500k;
# 带宽限制,对单个连接限速
limit_rate 50k;
}
limit_rate_after 500k :表示传输的前500k数据不限速,500k之后再进行限速。
limit_rate 50k :对单个连接限速为50k/s。如果一个客户端发起两个连接,就是50k * 2。
3.5黑白名单
配置固定IP为黑名单,访问时会返回403 Forbidden。
nginx 是按照自上而下的顺序进行匹配,匹配到一个就不往下继续了,如遇到 return 指令时 return 指令还是会生效。
server {
listen 8080;
server_name _;
location / {
allow 192.168.135.1;
deny all;
return 200 "$remote_addr 正常访问 3";
}
}
# 屏蔽单个ip访问
deny IP;
# 允许单个ip访问
allow IP;
# 屏蔽所有ip访问
deny all;
# 允许所有ip访问
allow all;
# 屏蔽整个段即从123.0.0.1到123.255.255.254访问的命令
deny 123.0.0.0/8
# 屏蔽IP段即从123.45.0.1到123.45.255.254访问的命令
deny 124.45.0.0/16
# 屏蔽IP段即从123.45.6.1到123.45.6.254访问的命令
deny 123.45.6.0/24
# 也可以通过配置文件来配置
include blockip.conf;
动态黑白名单:也可以采用Lua+Redis实现,将黑名单存入到Redis缓存,每次执行请求时,通过lua脚本先获取用户IP,匹配IP是否属于黑名单,如果是,则不让请求,如果不是,则放行。
/**
* provide by zym
* 0 error(s), 0 warning(s)
*/