有个老项目,通过 Squid 提供文件下载功能,利用 delay_parameters 实现带宽控制,问题是我玩不转 Squid,于是盘算着是不是能在 Nginx 里找到类似的功能。
好消息是 Nginx 提供了 limit_rate 和 limit_rate_after,举个例子来说明一下:
location /download/ {
limit_rate_after 500k;
limit_rate 50k;
}
大概意思是:用户下载达到 500k 后,便控制其速度在 50k 以内。
坏消息是此控制是针对单个连接而言的。换言之,只能限制单个连接的带宽,不能限制总带宽。不过使用 limit_conn 模块的话,可以在一定程度上缓解问题:
limit_conn_zone $server_name zone=servers:10m;
server {
location /download/ {
limit_conn servers 1000;
limit_rate_after 500k;
limit_rate 50k;
}
}
通过 limit_conn 限制了并发连接数,于是也就限制了总带宽。可惜这个解决方式并不完美,大家可以设想如下的例子:1000 个用户能够同时以 50k 的速度下载;那么在总带宽不变的情况下,2000 个用户是否能够同时以 25k 的速度下载?从业务的角度看,答案自然是肯定的,可实际上 limit_conn 和 limit_rate 不够灵活,无法简单实现此类逻辑。
当然,问题肯定有办法解决。比如使用第三方模块:limit_speed;还可以使用 Linux 内置的 TC 命令。limit_speed 比较简单,不多说了,接下来我们就看看 TC 的用法吧:
shell> tc qdisc add dev eth0 root handle 1: htb default 10
shell> tc class add dev eth0 parent 1: classid 1:1 htb rate 10mbit
shell> tc filter add dev eth0 protocol ip parent 1:0 prio 1 \
u32 match ip dport 80 0xffff flowid 1:1
本文介绍了多个 Nginx 限制访问方面的模块,实际上还有一个 limit_req 模块也超赞,虽然和本文关系不大,但建议大家了解,具体可以参考「nginx limit_req限速设置」
#以用户二进制IP地址,定义三个漏桶,滴落速率1-3req/sec,桶空间1m,1M能保持大约16000个(IP)状态
limit_req_zone $binary_remote_addr zone=qps1:1m rate=1r/s;
limit_req_zone $binary_remote_addr zone=qps2:1m rate=2r/s;
limit_req_zone $binary_remote_addr zone=qps3:1m rate=3r/s;
server {
#速率qps=1,峰值burst=5,延迟请求
#严格按照漏桶速率qps=1处理每秒请求
#在峰值burst=5以内的并发请求,会被挂起,延迟处理
#超出请求数限制则直接返回503
#客户端只要控制并发在峰值[burst]内,就不会触发limit_req_error_log
# 例1:发起一个并发请求=6,拒绝1个,处理1个,进入延迟队列4个:
#time request refuse sucess delay
#00:01 6 1 1 4
#00:02 0 0 1 3
#00:03 0 0 1 2
#00:04 0 0 1 1
#00:05 0 0 1 0
location /delay {
limit_req zone=qps1 burst=5;
}
#速率qps=1,峰值burst=5,不延迟请求
#加了nodelay之后,漏桶控制一段时长内的平均qps = 漏桶速率,允许瞬时的峰值qps > 漏桶qps
#所以峰值时的最高qps=(brust+qps-1)=5
#请求不会被delay,要么处理,要么直接返回503
#客户端需要控制qps每秒请求数,才不会触发limit_req_error_log
# 例2:每隔5秒发起一次达到峰值的并发请求,由于时间段内平均qps=1 所以仍然符合漏桶速率:
#time request refuse sucess
#00:01 5 0 5
#00:05 5 0 5
#00:10 5 0 5
# 例3:连续每秒发起并发请求=5,由于时间段内平均qps>>1,超出的请求被拒绝:
#time request refuse sucess
#00:01 5 0 5
#00:02 5 4 1
#00:03 5 4 1
location /nodelay {
limit_req zone=qps1 burst=5 nodelay;
}
}
|