Nginx限流控制

最近公司给客户做的系统遇到大量爬虫爬取,造成系统资源消耗增高、系统响应降低……

因为项目时间比较紧,没有更多时间在功能和代码层面上进行修改来上一套反爬虫系统。权衡以后决定使用Nginx提供的请求限流功能来实现一个简单的反爬虫机制。待以后再详细规划、设计反爬虫系统。

我们一直使用的是阿里发布的Nginx重发行版:Tengine,本文也将基于Tengine来介绍Nginx的限流控制。

ngx_http_limit_req_module 模块

Tengine提供了 ngx_http_limit_req_module 模块来实现HTTP请求的限流控制,通过定义的 键值来限制请求处理的频率。特别的,它可以限制来自单个IP地址的请求处理频率。 限制的方法是通过一种“漏桶”的方法——固定每秒处理的请求数,推迟过多的请求处理。提供的主要指令有:

  • **limit_req_log_level**:日志级别设置
  • **limit_req_zone**:设置一块共享内存限制域的参数,它可以用来保存键值的状态。 它特别保存了当前超出请求的数量。 键的值就是指定的变量(空值不会被计算)。
  • **limit_req**:设置对应的共享内存限制域和允许被处理的最大请求数阈值。 如果请求的频率超过了限制域配置的值,请求处理会被延迟,所以 所有的请求都是以定义的频率被处理的。 超过频率限制的请求会被延迟,直到被延迟的请求数超过了定义的阈值 这时,这个请求会被终止,并返回503 (Service Temporarily Unavailable) 错误。这个阈值的默认值等于0。
  • **limit_req_whitelist**:白名单,可以设置不应用 limit_req 限制的IP地址

limit_req_log_level(日志级别)

  • 语法:limit_req_log_level info | notice | warn | error;
  • 默认值:limit_req_log_level error;
  • 上下文:http, server, location

这个指令是设置请求限流控制的日志级别的,设置非常简单,就不在做单独介绍。

limit_req_zone(限制域的参数)

  • 语法:limit_req_zone $variable zone=name:size rate=rate;
  • 上下文:http
1
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

这里,状态被存在名为“one”,最大10M字节的共享内存里面。对于这个限制域来说 平均处理的请求频率不能超过每秒一次。

键值是客户端的IP地址。 如果不使用$remote_addr变量,而用$binary_remote_addr变量, 可以将每条状态记录的大小减少到64个字节,这样1M的内存可以保存大约1万6千个64字节的记录。 如果限制域的存储空间耗尽了,对于后续所有请求,服务器都会返回 503 (Service Temporarily Unavailable)错误。

请求频率可以设置为每秒几次(r/s)。如果请求的频率不到每秒一次, 你可以设置每分钟几次(r/m)。比如每秒半次就是30r/m。

limit_req(限制域)

  • 语法:limit_req zone=name [burst=number] [nodelay];
  • 上下文:http, server, location

limit_req_whitelist(白名单)

  • 语法:limit_req_whitelist geo_var_name=var_name geo_var_value=var_value
  • 上下文:http, server, location

另外,还可以使用 **geo** 指令定义变量来设置白名单,白名单内的地址将不受限流的控制。

1
2
3
4
5
6
7
geo $white_ip {
ranges;
default 0;
127.0.0.1-127.0.0.255 1;
}

limit_req_whitelist geo_var_name=white_ip geo_var_value=1;

上面表示IP在 127.0.0.1~127.0.0.255范围的地址会跳过 limit_req 的处理。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
geo $white_ip {
ranges;
default 0;
127.0.0.1-127.0.0.255 1;
}

limit_req_whitelist geo_var_name=white_ip geo_var_value=1;
limit_req_zone $binary_remote_addr zone=one:3m rate=1r/s;
limit_req_zone $binary_remote_addr $uri zone=two:3m rate=1r/s;
limit_req_zone $binary_remote_addr $request_uri zone=three:3m rate=1r/s;

location / {
limit_req zone=one burst=5;
limit_req zone=two forbid_action=@test1;
limit_req zone=three burst=3 forbid_action=@test2;
}

location /off {
limit_req off;
}

location @test1 {
rewrite ^ /test1.html;
}

location @test2 {
rewrite ^ /test2.html;
}

以上示例3个域限制策略,分别是:

  1. limit_req_zone $binary_remote_addr zone=one:3m rate=1r/s;:相同IP每秒只能发起一次请求。
  2. limit_req_zone $binary_remote_addr $uri zone=two:3m rate=1r/s;:相同IP访问同一资源每秒请求频率为1次。
  3. limit_req_zone $binary_remote_addr $request_uri zone=three:3m rate=1r/s;:同上,但请求URI是整个字符串,保住参数。

limit_req用来启用限制,一般放在 location 段内。支持开关,默认是打开状态。并且一个location支持多个limit_req指令,当有多个limit_req指令的话,这些指令是或的关系,也就是当其中任意一个限制被触发,则执行对应的limit_req。

burst设置允许超过频率限制的请求数,这里设置为3。

forbid_action 表示当条件被触发时,nginx所要执行的动作,支持name location和页面(/),默认是返回503。

总结

Tengine和Nginx官方对于限流控制的文档见:

http://tengine.taobao.org/document_cn/http_limit_req_cn.html

http://tengine.taobao.org/nginx_docs/cn/docs/http/ngx_http_limit_req_module.html

http://tengine.taobao.org/nginx_docs/cn/docs/http/ngx_http_limit_conn_module.html

在时间紧、对反爬虫机制要求不高时,可以先使用Nginx的限流控制来实现一个简单的反爬虫机制。待后期再详细规划、设计更完善的反爬虫系统。