概述:
当我们的程序上线之后,会遇到请求量太大,或者恶意请求的问题,这样我们就需要对我们的程序进行限流。当然限流的操作有很多种,可以使用后端程序进行限流,也可以使用Nginx进行限流。具体的限流方案根据项目情况而定,这里我推荐使用Nginx进行限流,因为使用后端程序进行限流会消耗我们的后端资源。下面讲解具体操作步骤
第一步:配置Nginx
这里配置的是nginx.conf文件
http {
keepalive_timeout 75s;
sendfile on; #当nginx代理的是静态文件服务的时候,打开这个会提升性能
tcp_nopush on; #启用linux上的tcp_cork套接字选项,它只能配合sendfile一起使用
# 根据ip限制速率,zone=名称:(桶)大小,放不下会丢弃请求,rate=同IP 5次/s ,平均200ms/次
limit_req_zone $binary_remote_addr zone=ratelimit:30m rate=5r/s;
server {
listen 80; #监听端口
server_name my.domain.cn; #域名
charset utf-8; #设置字符集
root /opt/web/order/dist; #vue项目路径
location / {
#burst是突发流量先不拒绝,单ip允许额外5个请求放入队列,nodelay降低突发流量排队,只要队列里有数据,就马上分配worker处理
limit_req zone=ratelimit burst=5 nodelay;
#当限制时,返回的状态码503 不可访问
limit_conn_status 503;
index /index.html;
try_files $uri $uri/ /index.html;
}
}
}
第二步:配置详解
1、limit_req_zone 用于设置限流和共享内存区域的参数,格式为:limit_req_zone key zone rate。
2、$binary_remote_addr 是 nginx 中的变量,表示基于 remote_addr(客户端IP) 来做限流,binary_ 是二进制存储。使用 $binary_remote_addr 而不是 $remote_addr 是因为二进制存储可以压缩内存占用量。 $remote_addr 变量的大小从7到15个字节不等,而 $binary_remote_addr变量的大小对于 IPv4 始终为4个字节,对于 IPv6 地址则为16个字节。
3、zone:定义共享内存区来存储访问信息,访问信息包括每个 IP 地址状态和访问受限请求 URL 的频率等。zone 的定义又分为两个部分:由 zone= 关键字标识的区域名称,以及冒号后面的区域大小。myLimit:10m 表示一个大小为10M,名字为 myLimit 的内存区域。1M 能存储16000个 IP 地址的访问信息,myLimit 大概可以存储约160000个地址。nginx 创建新记录的时候,会移除前60秒内没有被使用的记录,如果释放的空间还是存储不了新的记录,会返回503的状态码。
4、rate:设置最大的访问速率。rate=2r/s(为了好模拟,rate 设置的值比较小),表示每秒最多处理 2 个请求。事实上 nginx 是以毫秒为粒度追踪请求的,rate=2r/s 实际上是每500毫秒1个请求,也就是说,上一个请求完成后,如果500毫秒内还有请求到达,这些请求会被拒绝(默认返回503,如果想修改返回值,可以设置limit_req_status)。
这里需要注意的是Nginx是根据毫秒来定义请求限制的,比如我一秒允许两个请求,那么第二次请求必须要在500ms之后,否则就会被限流
5、burst :表示在超过设定的访问速率后能额外处理的请求数。当 rate=2r/s 时,表示每500ms 可以处理一个请求。burst=5时,如果同时有10个请求到达,nginx 会处理第1个请求,剩余9个请求中,会有5个被放入队列,剩余的4个请求会直接被拒绝。然后每隔500ms从队列中获取一个请求进行处理,此时如果后面继续有请求进来,如果队列中的请求数目超过了5,会被拒绝,不足5的时候会添加到队列中进行等待。
6、nodelay :表示不延迟。设置 nodelay 后,第一个到达的请求和队列中的请求会立即进行处理,不会出现等待的请求。
7、limit_req:这个对应的是第一点的配置,ratelimit对应的也是第一点的zone=ratelimit。
8、limit_conn_status:定义触发限流时返回的状态码。
所以以上配置就是当80端口进来的时候,所有的请求都会被限制为同一个ip,1秒只能五次,同时缓存了五次,并且不延迟,也就是我们可以在一秒内任何时候都可以请求五次,并且及时返回结果;
番外篇:配置ip白名单、ip连接数
ip白名单:
geo $limit {
default 1;
10.0.0.0/8 0;
192.168.0.0/24 0;
}
map $limit $limit_key {
0 "";
1 $binary_remote_addr;
}
limit_req_zone $limit_key zone=mylimit:10m rate=2r/s;
解释说明:geo 指令可以根据 IP 创建变量 $limit。$limit 的默认值是1,如果匹配到了下面的 IP,则返回对应的值(这里返回的是0)。之后通过 map 指令,将 $limit 的值映射为 $limit_key:在白名单内的,$limit_key 为空字符串,不在白名单内的,则为 $binary_remote_addr。当limit_req_zone指令的第一个参数是一个空字符串,限制不起作用,因此白名单的 IP 地址(在10.0.0.0/8和192.168.0.0/24子网中)没有被限制,其它 IP 地址都被限制为 2r/s。
限制ip连接数量:
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
server {
location ~* \.(html)$ {
limit_conn perip 10;
limit_conn perserver 100;
}
}
解释说明:imit_conn perip 10:key 是 $binary_remote_addr,表示限制单个IP同时最多能持有10个连接。
limit_conn perserver 100: key 是 $server_name,表示虚拟主机(server) 同时能处理并发连接的总数为100。
需要注意的是:只有当 request header 被后端server处理后,这个连接才进行计数。