具有多个漏孔的漏桶原理(Nginx+lua实现)
为了解决Nginx自带限流模块漏桶的不完美,我们要实现的是有n个漏孔的漏桶:
1个漏孔是从nosql缓存中获得数据,
1个漏孔是从静态文件中获得数据,
大大增加漏桶的容量,
上面这样的漏桶, 不但出水量增加, 而且桶的容量也大大增加。 实现这样的漏桶, 我们采用下面这样的设计:
改造前:
左边是普通的漏桶原理, 保证对微服务+MysqL是平缓的,我们不能把桶的容量设得过大。超出桶容量的请求 将被拒绝。由于桶很小, 很多请求会被拒绝掉,用户体验非常不好。服务器抗并发能力也并不高。
改造后: 右边是Nginx+lua实现的多级缓存降级,可以在多种降级方案(MysqL,nosql,静态文件)中自由切换,配备多个出水 口,这样桶的容量和每秒出水量都增加了不少。
实现
到git下载自动降级的 lua-resty-limit-traffic 模块
git clone https://github.com/openresty/lua-resty-limit-traffic
-- 加载Nginx—lua限流模块 local limit_req = require "resty.limit.req" -- 这里设置rate=50个请求/每秒,漏桶桶容量设置为1000个请求 -- 因为模块中控制粒度为毫秒级别,所以可以做到毫秒级别的平滑处理 local lim, err = limit_req.new("my_limit_req_store", 50, 1000) if not lim then ngx.log(ngx.ERR, "Failed to instantiate a resty.limit.req object: ", err) return ngx.exit(501) end local key = ngx.var.binary_remote_addr local delay, err = lim:incoming(key, true) ngx.say("计算出来的延迟时间是:") ngx.say(delay) --if ( delay <0 or delay==nil ) then --return ngx.exit(502) --end -- 1000以外的就溢出 if not delay then if err == "rejected" then return ngx.say("1000以外的就溢出") -- return ngx.exit(502) end ngx.log(ngx.ERR, "Failed to limit req: ", err) return ngx.exit(502) end -- 50-100的等待从微服务+MysqL获取实时数据;(100-50)/50 =1 if ( delay >0 and delay <=1 ) then ngx.say("第50-第100个 等待0-1秒后 从MysqL获取数据") ngx.sleep(delay) -- 100-400的直接从redis获取实时性略差的数据;(400-50)/50 =7 elseif ( delay >1 and delay <=7 ) then local resp, err = redis_instance:get("redis_goods_list_advert") ngx.say("第100-第400个 降级为从redis获取数据") ngx.say(resp) return -- 400-1000的从静态文件获取实时性非常低的数据(1000-50)/50 =19 elseif ( delay >7) then ngx.say("第400-第1000个 降级为从静态文件(死页面)获取数据") ngx.header.content_type="application/x-javascript;charset=utf-8" local file = "/etc/Nginx/html/goods_list_advert.json" local f = io.open(file, "rb") local content = f:read("*all") f:close() ngx.print(content) return end ngx.say("进入查询微服务+MysqL(最实时的数据,进入这里就是没降级)")
Nginx配置
user nobody; worker_processes 1; events { worker_connections 1024; } http { #/usr/share/lua/5.1/lua-resty-limit-traffic-master/lib/?.lua;; lua_package_path "/usr/share/lua/5.1/lua-resty-limit-traffic-master/lib/?.lua;;/usr/share/lua/5.1/lua-resty-redis/lib/?.lua;;/usr/share/lua/5.1/lua-resty-redis-cluster/lib/resty‘7/?.lua;;"; lua_package_cpath "/usr/share/lua/5.1/lua-resty-redis-cluster/lib/libredis_slot.so;;"; include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; lua_shared_dict my_limit_req_store 100m; server { listen 80; server_name 127.0.0.1; server_name 192.168.232.200; #获取广告推荐数据 location /goods_list_advert { default_type 'application/x-javascript;charset=utf-8'; content_by_lua_file /etc/Nginx/lua/goods_list_advert.lua; } #从服务层+MysqL获取数据 location /goods_list_advert_from_data { #allow 127.0.0.1; #deny all; default_type 'application/x-javascript;charset=utf-8'; #rewrite https//www.baidu.com/ break; proxy_pass http://192.168.232.201:18306/goods/getList; #content_by_lua ' # ngx.say("从服务层+MysqL获取数据") #'; } } }