nginx针对单个IP限制并发连接,限流实战
简介
最近在流量高峰期时,后端数据库服务器面临非常大的压力.mysql被大量的update.select语句挤爆,慢查询记录能瞬间达千条以上.导致业务不能正常访问.监控平台不断报警.
nginx有自带的模块可以针对每一个IP限制并发连接数..还可以限制每一个IP在单位时间内的request请求频率.
模块介绍
ngx_http_limit_conn_module : 官方文档 Used to limit the number of connections per the defined key, in particular, the number of connections from a single IP address.
limit_conn模块用来限制IP的并发请求连接.
ngx_http_limit_req_module : 官方文档 Used to limit the request processing rate per a defined key, in particular, the processing rate of requests coming from a single IP address
limit_req模块用来限制IP在单位时间内request请求频率
可以看到,limit_conn模块用来限制单个IP的并发连接.limit_req模块用来限制单IP入站请求的频率
实战环境
nginx版本: nginx/1.12.2
操作系统: Cetntos 7.4
拓扑: 阿里云SLB+3台nginx ECS服务器
配置
一.limit_conn模块限制IP并发连接
配置方法非常简单.
1.在nginx.conf的http字段中添加以下一行配置:
1 | http { |
命令格式:
limit_conn_zone :指令.在Nginx 1.1.8以后用此指令替代了此前的limit_conn指令
zone: 表示一个命名空间.
perip:表示一个名字,这个名字可以随意取.
10m: 存储IP地址的内存地址空间,一个Ipv4的地址大概占据64bit的空间.详情可见官方文档说明
$http_x_forwarded_for: 这个是根据实际的日志格式来获取IP地址.一般情况下这里应该是$remote_addr.
但是我们的日志格式为:
1 | http { |
日志记录格式:
1 | 100.117.85.149 - - [03/Aug/2018:13:15:21 +0800] "GET / HTTP/1.0" 200 28632 "-" "-" "ApacheBench/2.3"100.117.85.149 10.27.3.27 unix:/run/php-fpm/php-fpm.sock "27.115.51.xx" 0.237 "0.237" |
在我们的日志格式里.第一个IP字段(100.117.85.149)是个私网地址,是阿里的SLB的反代地址.而倒数第三个字段的IP地址(我隐去了最后一位IP字段)才是真正的阿里云SLB传递的客户端真实IP.
所以需要根据http_x_forwarded_for字段来限制IP
如果nginx是作为最前端的web服务器.那么默认的main日志格式应该是:
1 | http { |
日志记录:
1 | 10.0.4.241 - - [03/Aug/2018:02:20:41 +0800] "GET / HTTP/1.0" 200 225 "-" "ApacheBench/2.3" "-" |
这里第一个字段remote_addr是客户端的真实IP地址.那么上面的指令应该改成:
1 | http { |
NOTE:官方文档建议使用$binary_remote_addr替代$remote_addr.这样好处是可以节省内存存储空间.
但是我尝试过$binary_http_x_forwarded_for并不能被nginx识别:
1
2
3
4 > [work@tongji-1 nginx]$ sudo nginx -t
> nginx: [emerg] unknown "binary_http_x_forwarded_for" variable
> nginx: configuration file /etc/nginx/nginx.conf test failed
>
由此可见.该指令需要根据实际日志格式中,真实IP所在的字段位置和变量名来具体配置.
2.在虚拟主机配置文件中调用该指令:
1 | #隐去了敏感配置信息 |
指令格式:
limit_conn perip 5;
limit_conn: 模块指令,改指令可以用在http,server以及location字段.代表的作用域分别是:全局nginx,全局server_name,以及某个路径
perip:调用nginx.conf的perip zone
5: 允许最大的并发数为5
测试效果
在另外一台服务器用ab进行一次测试:
1 | [root@kong ~]# ab -n 50 -c 20 http://www.xxxxxx.com/ |
ab模拟了20个并发,一共50个request请求的压测.从输出结果的failed requests字段来看,有29个请求失败.结果并不是十分精准.而且每次执行统一的步骤得出的结果并不一致.
查看nginx服务器的access访问日志输出:
1 | #为了隐私,隐去了IP地址最后一位,以及网站域名 |
可见Nginx阻止了很多请求,并且返回503错误.这表示验证是成功.Ningx确实能阻止部分的并发请求.
Note:
- 根据官网文档的解释来看,一旦nginx分配给Limit_conn模块的10M内存空间消耗完,就对于所有流量的请求都返回503错误
- 个人认为这个模块并不十分成熟,至少我在公司,家里的2个测试环境都没有成功.而且尝试过nginx多个版本,多个系统平台上都没有成功.
二.IP限流配置
配置方式和上文中大同小异,只是具体的指令不同.所以部分配置不再详细解释
1.在nginx.conf配置文件的http字段中添加如下一行:
1 | http { |
解析:
rate=1r/s 表示每秒的请求次数为1次.
2.在虚拟主机配置下添加一行:
1 | server { |
解析:
1 | limit_req指令可以放在server,http或者location字段 |
测试
在另外一台服务器执行访问nginx服务
1 | [root@kong ~]# curl -i http://www.test.com/index.html |
测试可见,第一次请求是正常的..但是当1秒内第二次请求时就返回503错误了.提示503 Service Temporarily Unavailable
用ab测试结果如下:
1 | [root@kong ~]# ab -n 10 -c 2 http://www.test.com/ |
我个人认为这个模块还是非常准确的.发起10次请求里,有8次失败.