在Nginx中有2个模块可以实现对客户端请求进行限制,当请求频率达到限制将进行拦截并返回503状态码。通过这2个模块可以达到一定的攻击防护作用:
1、ngx_http_limit_conn_module:限制同一时间连接数,即并发连接数限制
2、ngx_http_limit_req_module:限制一定时间内的请求数,优先级高于ngx_http_limit_conn_module
由于一个TCP连接可以产生多个请求,所以实际应用中对单位时间内的请求做限制比对并发连接做限制的效果更好。所以建议使用ngx_http_limit_req_module(二者在配置方式基本是一样的)。该模块默认已经安装,可以直接在配置文件的http字段中进行定义,然后在server或location字段中引用(和upstream与proxy很相似)。
配置示例:
http {
limit_req_zone $binary_remote_addr zone=test:10m rate=1r/s; #定义限制规则
...
server {
...
location /search/ {
limit_req zone=one burst=5; #引用规则
}
配置说明:
1、在http字段中定义了limit_req-zone访问限制模块,内容如下:
limit_req_zone $binary_remote_addr zone=test:10m rate=1r/s;
$binary_remote_addr是$remote_addr(客户端IP)的二进制格式,固定占用4个字节。而$remote_addr按照字符串存储,占用7-15个字节。这样看来用$binary_remote_addr可以节省空间。
zone=:区域名称,可自定义,这里写的是test,占用空间大小为10m
rate=:平均处理的请求数,这里定义的是不能超过每秒一次。
2、在server字段中做如下配置(可以针对location做限制),这里是对客户端访问请求后缀为html的页面进行限流
location ~* .html$ {
limit_req zone=test burst=5 nodelay;
}
zone=:和http字段中的zone定义必须一致
burst=:可以理解为缓冲队列
nodelay:对用户发起的请求不做延迟处理,而是立即处理。比如上面定义了rate=1r/s,即每秒钟只处理1个请求。如果同一时刻有两个后缀为htm的请求过来了,若设置了nodelay,则会立刻处理这两个请求。若没设置nodelay,则会严格执行rate=1r/s的配置,即只处理一个请求,然后下一秒钟再处理另外一个请求。直观的看就是页面数据卡了,过了一秒后才加载出来。
真正对限流起作用的配置就是rate=1r/s和burst=5这两个配置,参考具体案例以便理解:
有两个请求同时到达Nginx,其中一个被处理,另一个放到了burst缓冲队列里。由于配置了nodelay,所以第二个请求依然被处理了,但会占用burst缓冲队列的一个长度。如果下一秒没有请求过来,这一个长度的空间就会被释放,否则会继续占用burst队列。当burst空间占用达到设置的5之后所有请求就会直接被Nginx拒绝,并返回503错误。可见如果第二秒又来了两个请求,其中一个请求又占用了一个burst空间,第三秒、第四秒直到第五秒,每秒都有两个请求过来,虽然两个请求都被处理了(因为配置了nodelay),但其中一个请求仍然占用了一个burst长度,五秒后整个burst长度=5都被占用了。第六秒再过来两个请求,其中一个请求就被拒绝了。被拒绝的请求在Nginx错误日志中可以看到是被某个zone给拒绝了。
对于超出限制的请求将会被拒绝掉,而且错误日志里会有记录: