简单的Nginx防CC方式
实验
Nginx配置
http { limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; server { #限制每ip每秒不超过20个请求,漏桶数burst为5 #brust的意思就是,如果第1秒、2,3,4秒请求为19个, #第5秒的请求为25个是被允许的。 #但是如果你第1秒就25个请求,第2秒超过20的请求返回503错误。 #nodelay,如果不设置该选项,严格使用平均速率限制请求数, #第1秒25个请求时,5个请求放到第2秒执行, #设置nodelay,25个请求将在第1秒执行。 limit_req zone=one burst=1 nodelay; }}
上面样本的配置是什么意思呢?
详细的可以参考官方说明文档:Module ngx_http_limit_req_module(http://nginx.org/en/docs/http/ngx_http_limit_req_module.html)
模拟请求
这里我们需要Apache Benchmark这个小工具来生成请求
//1个用户持续100s的时间向服务器发送请求
ab -t 100 -c 1 -vvv http://example.com/
Nginx配置样本一
http { limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; server { limit_req zone=one burst=1 nodelay; }}
ab测试结果如下所示:
数据 | 成功的请求数 | 失败的请求数 | 请求时间 | 每秒成功的请求数 |
---|
1 | 100 | 19438 | 101.195 | 0.98 |
2 | 100 | 17651 | 100.655 | 0.99 |
3 | 97 | 25735 | 100.424 | 0.96 |
4 | 101 | 26791 | 100.000 | 1.01 |
5 | 98 | 19051 | 100.514 | 0.98 |
平均 | 99 | 21733.2 | 100.557 | 0.98 |
以上失败的请求在Nginx上生成的错误日志如下显示
2015/05/0912:48:57 [error] 6564#0: *2219 limiting requests, excess: 1.273 by zone "one", client: 10.0.2.2, server: example.com, request: "GET / HTTP/1.0", host: "example.com"2015/05/0912:48:57 [error] 6564#0: *2220 limiting requests, excess: 1.272 by zone "one", client: 10.0.2.2, server: example.com, request: "GET / HTTP/1.0", host: "example.com"2015/05/0912:48:57 [error] 6564#0: *2221 limiting requests, excess: 1.271 by zone "one", client: 10.0.2.2, server: example.com, request: "GET / HTTP/1.0", host: "example.com"2015/05/0912:48:57 [error] 6564#0: *2222 limiting requests, excess: 1.270 by zone "one", client: 10.0.2.2, server: example.com, request: "GET / HTTP/1.0", host: "example.com"2015/05/0912:48:57 [error] 6564#0: *2223 limiting requests, excess: 1.269 by zone "one", client: 10.0.2.2, server: example.com, request: "GET / HTTP/1.0", host: "example.com"2015/05/0912:48:57 [error] 6564#0: *2224 limiting requests, excess: 1.268 by zone "one", client: 10.0.2.2, server: example.com, request: "GET / HTTP/1.0", host: "example.com"
如上ab测试中如果是失败的请求,nginx的limit_req模块会统一返回503给客户端,浏览器上面显示的是这个样子的。
Nginx配置样本二
在配置二里面,我把burst(峰值)提高到了10
http { limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; server { limit_req zone=one burst=10 nodelay; }}
数据 | 成功的请求数 | 失败的请求数 | 请求时间 | 每秒成功的请求数 |
---|
1 | 110 | 19042 | 100.144 | 1.09 |
2 | 111 | 22271 | 101.714 | 1.09 |
3 | 111 | 18466 | 100.504 | 1.10 |
4 | 111 | 16468 | 101.285 | 1.09 |
5 | 111 | 12770 | 100.596 | 1.10 |
平均 | 110 | 17803 | 100.788 | 1.09 |
从数据来看,提高了burst值,明显nginx成功的请求数上去了。
Nginx配置样本三
在样本二的基础上,我们把nodelay去除掉
http { limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; server { limit_req zone=one burst=10; }}
数据 | 成功的请求数 | 失败的请求数 | 请求时间 | 每秒成功的请求数 |
---|
1 | 96 | 0 | 100.223 | 1.09 |
2 | 98 | 0 | 100.238 | 0.97 |
3 | 100 | 0 | 100.761 | 0.99 |
4 | 96 | 0 | 100.074 | 0.95 |
5 | 97 | 0 | 100.021 | 0.96 |
平均 | 97.4 | 0 | 100.263 | 0.97 |
从这里的数据可以看到将nodelay的参数去掉的话,成功的请求数在100左右而失败的请求数变成0了,为什么呢?
存在的问题
虽然用limit_req_module可以一定上的防止CC攻击,但是有误杀概率;国内宽带用户的IP地址已经大量内网化,几百人共享一个IP的可能性是很大的。