一、Nginx的请求限制
1.1 请求限制
限制主要有两种类型:
- 连接限制: limit_conn_module
- 请求限制: limit_req_module
对于连接限制 limit_conn_module模块
该ngx_http_limit_conn_module模块用于限制每个定义的密钥的连接数量,特别是来自单个IP地址的连接数量。
并非所有连接都被计算在内 ,只有在服务器处理请求并且已经读取了整个请求头时才计算连接。
- 连接频率限制的配置:
Syntax: limit_conn_zone key zone=name:size;
Default: —
Context: http
#可理解在内存中开辟一个区域对于指定的nginx变量(key,例如:binary_remote_addr)进行限制。name表示申请的空间的名字,size表示申请空间的大小。
Syntax: limit_conn zone number;
Default: —
Context: http, server, location
#第二部分zone就是第一部分设置的名字name,number表示进行并发的限制。例如设置为1,表示一个时间段只能有一个。
- 用法
http {
# ...其它代码省略...
# 开辟一个10m的连接空间,命名为addr
limit_conn_zone $binary_remote_addr zone=addr:10m;
server {
...
location /download/ {
# 服务器每次只允许一个IP地址连接
limit_conn addr 1;
}
}
}
请求限制 ngx_http_limit_req_module模块
用于限制每一个定义的密钥的请求的处理速率,特别是从一个单一的IP地址的请求的处理速率。限制是使用“泄漏桶”方法完成的
- 请求限制的配置:
Syntax: limit_req_zone key zone=name:size rate=rate;
Default: —
Context: http
#第一部分配置和连接配置相似,rate表示速率,以秒s为单位(rate=1r/s)
Syntax: limit_req zone=name [burst=number] [nodelay];
Default: —
Context: http, server, location
#第二部分zone就是第一部分设置的名字name,[]内为可配置选项。
- 用法
http {
# ...其它代码省略...
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
#开辟一个10m的请求空间,命名为one。同一个IP发送的请求,平均每秒只处理一次
#$binary_remote_addr :表示通过remote_addr这个标识来做限制,“binary_”的目的是缩写内存占用量,是限制同一客户端ip地址
#zone=one:10m:表示生成一个大小为10M,名字为one的内存区域,用来存储访问的频次信息
#rate=1r/s:表示允许相同标识的客户端的访问频次,这里限制的是每秒1次,即每秒只处理一个请求;比如30r/m的,即限制每2秒访问一次,即每2秒才处理一个请求。
server {
...
location /search/ {
# 1s同一个客户端只允许连接一次
limit_req zone=one;
# 当客户端请求超过指定次数,最多宽限3次请求,并延迟处理,1秒1个请求
# limit_req zone=one burst=3;
# 当客户端请求超过指定次数,最多宽限3次请求,并立即处理。
# limit_req zone=one burst=3 nodelay;
}
}
}
1.2 连接限制的配置
1、vim /usr/local/nginx/conf/nginx.conf
2、添加
limit_conn_zone $binary_remote_addr zone=conn_zone:1m;
limit_req_zone $binary_remote_addr zone=req_zone:1m rate=1r/s;
# rate=1r/s每秒只能执行1次请求, 多的直接返回503错误。
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
#limit_conn conn_zone 1;
#limit_req zone=req_zone burst=3 nodelay;
#limit_req zone=req_zone burst=3;
#limit_req zone=req_zone;
index index.html index.htm;
}
我们看到上面为定义zone,第二行是请求定义,表示对远程请求进行每秒一次的请求限制。这里的binary_remote_addr和remote_addr代表的含义是一样的(远程主机的IP)只是使用binary_remote_addr存储一个IP会比remote_addr省10个字节。burst 参数往后延迟3个请求 nodelay 立即返回
3、进行语法检测 nginx -t, 重新加载 nginx -s reload
4、客户端测试:ab -n 40 -c 20 http://172.25.2.2/,共访问页面40次,一次产生请求的并发数为20.
此时,我们可以看到40个访问都成功。
1)打开请求限制limit_req zone=req_zone
,1s同一个客户端只允许连接一次
再次在客户端测试,ab -n 40 -c 20 http://172.25.2.2/,我们可以看到只成功了1次,且1.015s就完成了压测。
我们查看/usr/local/nginx/logs/access.log
日志,看到只有一个请求成功,其他的都是直接返回503,即服务器拒绝了请求。
2)打开请求限制limit_req zone=req_zone burst=3
,
burst=3:burst爆发,这个配置是设置一个大小为3的缓冲区当有大量请求(爆发)过来时,超过了访问频次限制的请求可以先放到这个缓冲区内等待,但是这个等待区里的位置只有3个,超过的请求会直接报503的错误然后返回。 在完成峰值请求之后,缓冲队列不能再放入请求。如果rate=1r/s,且这段时间内没有请求再到来,则每1s 缓冲队列就能恢复一个缓冲请求的能力,直到恢复到能缓冲3个请求位置。
再次在客户端测试,ab -n 40 -c 20 http://172.25.2.2/,我们可以看到请求只成功了4次,且3.003s才完成压测。
我们查看/usr/local/nginx/logs/access.log
日志,看到只有4个请求成功,其他的都是直接返回503,即服务器拒绝了请求。
- 我们可观察到17分8秒时,即压测第1秒时,成功处理了1个请求,另外有36个请求瞬间返回了503,剩余的3个请求每隔1秒处理一次。这是因为设置了burst=3,在服务器接收到40个并发请求后,先处理1个请求,同时将3个请求放入burst缓冲队列中,等待处理。而超过burst等待区的数量的请求就被直接抛弃了,报错503然后直接返回。
此时,查看 /var/log/nginx/error.log
日志,可以看到36个limiting request
3)打开请求限制limit_req zone=req_zone burst=3 nodelay
,
nodelay: 如果设置,会在瞬时提供处理(burst + rate)个请求的能力,请求超过(burst + rate)的时候就会直接返回503,永远不存在请求需要等待的情况。(这里的rate的单位是:r/s);如果没有设置,则所有请求会依次等待排队。
加brust=3和nodelay的情况下, 有一个容量为3的缓冲区, rate=1r/s每秒能执行4次请求, 4=1+3,多的直接返回503错误。
再次在客户端测试,ab -n 40 -c 20 http://172.25.2.2/,我们可以看到压测在0.031秒内就完成了。
我们查看/usr/local/nginx/logs/access.log
日志,看到只有4个请求成功,其他的都是直接返回503。
- 可以发现在1s内,服务器端处理了4个请求(峰值速度:burst+原来的处理速度)。对于剩下的36个请求,直接返回503,在下一秒如果继续向服务端发送40个请求,服务端会直接拒绝这40个请求并返回503。因为设定了每1s处理1个请求,所以直到3s 之后,才可以再处理一个请求,即如果此时向服务端发送40个请求,会返回39个503,一个200
二、Nginx的访问控制
nginx的访问控制主要分为两类:
- 基于IP的访问控制 http_access_module
- 基于用户的登录认证 http_auth_basic_module,此种方法我们不多解释。
对于http_access_module模块
- 配置
Syntax: allow address | CIDR | unix: | all;
Default: —
Context: http, server, location, limit_except
Syntax: deny address | CIDR | unix: | all;
# address:IP地址,例如:192.168.1.1
# CIDR:例如:192.168.1.0/24;
# unix:Socket方式
# all:所有
Default: —
Context: http, server, location, limit_except
- 用法
server {
# ...其它代码省略...
location ~ ^/index_1.html {
root /usr/share/nginx/html;
deny 172.25.2.250; # 拒绝这个IP访问
allow all; # 允许其他所有IP访问
}
location ~ ^/index_2.html {
root /usr/share/nginx/html;
allow 172.25.3.0/24; # 允许IP172.25.3.*访问
deny all; # 拒绝其他所有IP访问
}
}
编辑配置文件/usr/local/nginx/conf/nginx.conf
,
allow和deny会按照顺序, 从上往下, 找到第一个匹配规则, 判断是否允许访问, 所以一般把all放最后。
此时,从物理机(172.25.2.250)访问,可以看到没有权限。
从另一台虚拟机(172.25.2.4)访问,可以访问。
http_access_module模块的局限性:
nginx的访问控制限制是针对客户端的IP来进行限制的,但是nginx并不确定真正的客户端是哪个,凡是和nginx进行交互的都被当做是客户端。(remote_addr是直接和nginx通信的IP)如果我们访问不是直接访问到服务端而是由中间代理进行(如nginx,CDN等),访问控制这时就会失效。
- 局限性解决方法总结:
方法一: 采用http头信息控制访问,如HTTP_X_FORWARD_FOR。http_x_forwarded_for头信息控制访问 会更好的解决该问题,它要求访问时必须带上所有用到的ip的地址信息
http_x_forwarded_for记录过程:
http_x_forwarded_for = Client IP, Proxy(1)IP, Proxy(2)IP,...
方法二: 结合geo模块
方法三: 通过HTTP自定义变量传递