哈尔滨线下教学最新情况/通州优化公司
流量层限流:
Nginx之 IP 限流(限制同一ip单位时间访问数)
# $binary_remote_addr 关键字 表示通过remote_addr这个标识来做限制,“binary_”的目的是缩写内存占用量,是限制同一客户端ip地址
# zone=iplimit:20m 名叫iplimit的内存区域,大小为20兆 (用来记录访问频率信息)
# rate=1r/s 每秒放行一个请求 rate=100r/m 每分钟放行100个
limit_req_zone $binary_remote_addr zone=iplimit:20m rate=1r/s;# 服务级别限制
limit_req_zone $server_name zone=serverlimit:10m rate=10r/s;server {server_name www.test.com;location /app {proxy_pass http://127.0.0.1:8080;# 基于IP地址限制# zone=iplimit 引用 limit_req_zone # burst=2 设置大小为2的缓冲区 假设当前突然涌入三个请求 丢弃一个# nodelay 缓冲区满了直接返回503异常 如果没有设置,则所有请求会依次等待排队limit_req zone=iplimit burst=2 nodelay;limit_req zone=serverlimit burst=1 nodelay;}
}
这里的burst参数主要采用了令牌桶算法。令牌桶算法是网络流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常用的一种算法。典型情况下,令牌桶算法用来控制发送到网络上的数据数目,并允许突发数据的发送。
Nginx之 连接数 限流
# 连接数限制
limit_conn_zone $binary_remote_addr zone=conniplimit:10m;
limit_conn_zone $server_name zone=connserverlimit:10m;server {server_name www.test.com;location /app {proxy_pass http://127.0.0.1:8080;# 每个ip最多10个连接limit_conn conniplimit 10;# 每个server最多保持500个连接limit_conn connserverlimit 500;# 异常指定返回状态 504(默认503)limit_req_status 504;limit_conn_status 504;}
}
两种方式可以组合使用
server {server_name www.test.com;location /app {limit_req zone=iplimit burst=2 nodelay;limit_req zone=serverlimit burst=1 nodelay;limit_conn conniplimit 10;limit_conn connserverlimit 500;limit_req_status 504;limit_conn_status 504;}
}
接口层限流
使用Nginx+Lua实现的Lua脚本:
local locks = require "resty.lock"
local function acquire()local lock =locks:new("locks")local elapsed, err =lock:lock("limit_key") --互斥锁local limit_counter =ngx.shared.limit_counter --计数器local key = "ip:" ..os.time()local limit = 5 --限流大小local current =limit_counter:get(key)if current ~= nil and current + 1> limit then --如果超出限流大小lock:unlock()return 0endif current == nil thenlimit_counter:set(key, 1, 1) --第一次需要设置过期时间,设置key的值为1,过期时间为1秒elselimit_counter:incr(key, 1) --第二次开始加1即可endlock:unlock()return 1
end
ngx.print(acquire())
实现中我们需要使用lua-resty-lock互斥锁模块来解决原子性问题(在实际工程中使用时请考虑获取锁的超时问题),并使用ngx.shared.DICT共享字典来实现计数器。如果需要限流则返回0,否则返回1。使用时需要先定义两个共享字典(分别用来存放锁和计数器数据):
Java代码 收藏代码
http { …… lua_shared_dict locks 10m; lua_shared_dict limit_counter 10m;
}
有人会纠结如果应用并发量非常大那么redis或者nginx是不是能抗得住;不过这个问题要从多方面考虑:你的流量是不是真的有这么大,是不是可以通过一致性哈希将分布式限流进行分片,是不是可以当并发量太大降级为应用级限流;对策非常多,可以根据实际情况调节;像在京东使用Redis+Lua来限流抢购流量,一般流量是没有问题的。
对于分布式限流目前遇到的场景是业务上的限流,而不是流量入口的限流;流量入口限流应该在接入层完成,而接入层笔者一般使用Nginx。