服务接口API限流 Rate Limit 续

时间:2023-03-08 16:51:22

一、前言

一篇文章中粗浅的介绍使用Redis和基于令牌桶算法进行对服务接口API限流,本文介绍另一种算法---漏桶算法的应用。Nginx想必大家都有所了解是一个高性能的 HTTP 和反向代理服务器,优秀而强大的Nginx依然可以处理限制来自单个IP地址的请求处理频率。ngx_http_limit_conn_module模块可以限制请求数即通过定义的键值来限制请求处理的频率。该模块其采用漏桶算法,每秒固定处理请求数,推迟延迟请求。

二、ngx_http_limit_conn_module模块指令

  • limit_req_zone

语法: limit_req_zone $variable zone=name:size rate=rate;
        默认值: none
        配置段: http

说明:设置一块共享内存限制域用来保存键值的状态参数,尤其是保存了当前超出请求的数量。键的值就是指定的变量(空值不会被计算)。如:

limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s;

区域名称为one,大小为10m,平均处理的请求频率不能超过每秒一次。
键值是客户端IP。

使用$binary_remote_addr变量,可以将每条状态记录大小减小到64字节,十六万多条记录。

如果共享内存限制域的存储空间耗尽了,对于后续的所有请求,nginx都会返回503错误。

速度可以设置为每秒处理请求数或每分钟处理请求数,其值必须是整数。

  • limit_req_log_level

语法: limit_req_log_level info | notice | warn | error;
  默认值: limit_req_log_level error;
  配置段: http, server, location

说明:设置你所希望的日志级别,当服务器因为频率过高拒绝或者延迟处理请求时可以记下相应级别的日志。 延迟记录的日志级别比拒绝的低一个级别;比如, 如果设置“limit_req_log_level notice”, 延迟的日志就是info级别。

  • limit_req_status

语法: limit_req_status code
        默认值: limit_req_status 503
        配置段: http, server, location

说明:设置拒绝请求的响应状态码。

  • limit_req

语法: limit_req zone=name [burst=number] [nodelay];
        默认值: —
        配置段: http, server, location

说明:设置对应的共享内存限制域和允许被处理的最大请求数阈值。 如果请求的频率超过了限制域配置的值,请求处理会被延迟,所以所有的请求都是以定义的频率被处理的。 超过频率限制的请求会被延迟,直到被延迟的请求数超过了定义的阈值,这时,这个请求会被终止,并返回503 (Service Temporarily Unavailable) 错误。这个阈值的默认值为0。如:

limit_req_zone $binary_remote_addr zone=ttlsa_com:10m rate=5r/s;
server {
location /www.cnblogs.com/ {
limit_req zone=cnblogs_com burst=;
}
}

限制平均每秒不超过5个请求,同时允许超过频率限制的请求数不多于5个。
      如果不希望超过的请求被延迟,可以用nodelay参数,如:

limit_req zone=ttlsa_com burst= nodelay;

完整的配置

http {
limit_req_zone $binary_remote_addr zone=cnblogs_com:10m rate=1r/s; server {
location /www.cnblogs.com/ {
limit_req zone=cnblogs_com burst=;
}
}
}

从上述的配置中,可以看出对所有的IP都进行了限制。在某些特殊情况下,我们不希望对某些IP限制如公司IP,那么白名单就有了用武之地,将不需要限制的IP加入到白名单中即可。白名单实现方法,需要结合geo和map指令来实现。看配置:

geo $whiteiplist  {
default ;
127.0.0.1 ;
10.0.0.0/ ;
107.155.42.0/ ;
} map $whiteiplist $limit {
$binary_remote_addr;
"";
} limit_conn_zone $limit zone=limit:10m; server {
listen ;
server_name www.cnblogs.com; location ^~ /cnblogs.com/ {
limit_conn limit ;
limit_rate 200k;
}
}
}

说明:

1. geo指令定义一个白名单$whiteiplist, 默认值为1, 所有都受限制。 如果客户端IP与白名单列出的IP相匹配,则$whiteiplist值为0也就是不受限制。
2. map指令是将$whiteiplist值为1的,也就是受限制的IP,映射为客户端IP。将$whiteiplist值为0的,也就是白名单IP,映射为空的字符串。
3. limit_conn_zone和limit_req_zone指令对于键为空值的将会被忽略,从而实现对于列出来的IP不做限制。

三、题外话

对于肆无忌惮的蜘蛛或者大流量恶意的攻击访问,会带来带宽的浪费和服务器巨大的压力,影响业务的正常使用。

对于这些情况可以采取多种措施:

1、对同一个ip的连接数,并发数和请求书进行限制。

通过ngx_http_limit_conn_module和ngx_http_limit_conn_module模块可以很好的达到效果。

上周玩客被百度蜘蛛给盯上了,百度蜘蛛对玩客的抓取频率增加了5倍。百度蜘蛛抓取量骤增,导致服务器负载很高。最终用nginx的ngx_http_limit_req_module模块限制了百度蜘蛛的抓取频率。每分钟允许百度蜘蛛抓取200次,多余的抓取请求返回503。

nginx的配置:
#全局配置
limit_req_zone $anti_spider zone=anti_spider:60m rate=200r/m;
#某个server中
limit_req zone=anti_spider burst=5 nodelay;
if ($http_user_agent ~* “baiduspider”) {
set $anti_spider $http_user_agent;
}

参数说明:
指令linit_req_zone 中的rate=200r/m 表示每分钟只能处理200个请求。
指令limit_req 中的burst=5 表示最大并发为5。即同一时间只能同时处理5个请求。
指令limit_req 中的 nodelay 表示当已经达到burst值时,再来新请求时,直接返回503
IF部分用于判断是否是百度蜘蛛的user agent。如果是,就对变量$anti_spider赋值。这样就做到了只对百度蜘蛛进行限制了。

2、对恶意的IP访问攻击

首先要将恶意的IP挡在缓存之外,减小穿透缓存把压力打到DB上。

其次对于的恶意的IP进行封IP甚至封IP段。

ngx_http_access_module模块中的deny和allow指令

allow
语法:  allow address | CIDR | unix: | all;
默认值: —
配置段: http, server, location, limit_except

允许某个ip或者一个ip段访问.如果指定unix:,那将允许socket的访问.注意:unix在1.5.1中新加入的功能,如果你的版本比这个低,请不要使用这个方法。

deny
语法:  deny address | CIDR | unix: | all;
默认值: —
配置段:http, server, location, limit_except

禁止某个ip或者一个ip段访问.如果指定unix:,那将禁止socket的访问.注意:unix在1.5.1中新加入的功能,如果你的版本比这个低,请不要使用这个方法。

location / {
deny  107.155.42.59;
allow 107.155.42.60/66;
deny  all;
}

从上到下的顺序,匹配到了便跳出。如上的例子先禁止了107.155.42.59,接下来允许了1个网段,最后未匹配的IP全部禁止访问。在实际生产环境中,我们也会使用nginx 的geo模块配合使用。

deny和allow是nginx里面最基本的指令,如果想禁止谁访问就添加指令deny加上IP,想允许则加上指令allow ip;如果想禁止或者允许所有,那么allow all或者deny all即可。

参考:

http://www.bo56.com/%E4%BD%BF%E7%94%A8nginx%E9%99%90%E5%88%B6%E7%99%BE%E5%BA%A6%E8%9C%98%E8%9B%9B%E7%9A%84%E9%A2%91%E7%B9%81%E6%8A%93%E5%8F%96/

由于本人经验有限,文章中难免会有错误,请浏览文章的您指正或有不同的观点共同探讨!