上篇学习了经过Nginx模块进行限流的方法,接下来学习一下利用Nginx+Lua进行接入层限流html
Openresty提供了lua-resty-limit-traffic模块进行限流,模块实现了limit.conn和limit.req的功能和算法nginx
示例:git
http { lua_shared_dict my_limit_req_store 100m; server { location / { access_by_lua ' local limit_req = require "resty.limit.req" -- 限制请求速率为200 req/sec,而且容许100 req/sec的突发请求 -- 就是说咱们会把200以上300一下的请求请求给延迟 -- 超过300的请求将会被拒绝 local lim, err = limit_req.new("my_limit_req_store", 200, 100) if not lim then --申请limit_req对象失败 ngx.log(ngx.ERR, "failed to instantiate a resty.limit.req object: ", err) return ngx.exit(500) end -- 下面代码针对每个单独的请求 -- 使用ip地址做为限流的key local key = ngx.var.binary_remote_addr local delay, err = lim:incoming(key, true) if not delay then if err == "rejected" then return ngx.exit(503) end ngx.log(ngx.ERR, "failed to limit req: ", err) return ngx.exit(500) end if delay > 0 then -- 第二个参数(err)保存着超过请求速率的请求数 -- 例如err等于31,意味着当前速率是231 req/sec local excess = err -- 当前请求超过200 req/sec 但小于 300 req/sec -- 所以咱们sleep一下,保证速率是200 req/sec,请求延迟处理 ngx.sleep(delay) --非阻塞sleep(秒) end '; } } }
方法说明
new
语法: obj, err = class.new(shdict_name, rate, burst)
成功的话会返回resty.limit.req对象,失败的话返回nil和一个描述错误缘由的字符串值github
incoming
语法: delay, err = obj:incoming(key, commit)
key这里是指须要限流的ip;commit真心没看懂(囧),先按照例子传true
返回值根据状况的不一样返回不一样的值
1.若是请求没超过速率,那么delay和err返回0
2.若是请求超过速率但没超过“速率+burst”的值,那么delay将会返回一个合适的秒数,告诉你多久后这个请求才会被处理;第二个参数(err)保存着超过请求速率的请求数量
3.若是请求超过“速率+burst”的值,那么delay会返回nil,err会返回”rejected”字符串
4.若是一个error发生了,delay会返回nil,err会返回具体错误的字符串描述算法
inconing方法不会sleep本身,须要调用者调用’ngx.sleep’去延迟请求处理。
并发
http { ...... lua_shared_dict limit_req_store 10m; ...... }
配置nginx.conf学习
http { ...... server { location /resty-limit { access_by_lua_file /path/to/your/resty-limit.lua; echo "you success"; } } ...... }
把限流的配置放在nginx的access阶段,若是限流的话,不会输出you success
。测试
public class NginxLimit { public static void main(String[] args) throws IOException, InterruptedException { final NginxLimit distrubuteLimit = new NginxLimit(); final CountDownLatch latch = new CountDownLatch(1);//两个工人的协做 for (int i = 0; i < 10; i++) { final int finalI = i; Thread t = new Thread(new Runnable() { public void run() { try { latch.await(); String rev = distrubuteLimit.sendGet("http://localhost:9998/resty-limit", null); System.out.println(rev); } catch (InterruptedException e) { e.printStackTrace(); } } }); t.start(); } latch.countDown(); System.in.read(); } }
并发产生10个请求ui
ou success 发送GET请求出现异常 发送GET请求出现异常 发送GET请求出现异常 发送GET请求出现异常 发送GET请求出现异常 发送GET请求出现异常 you success you success you success
根据lua文件的配置,10个请求里应该有5个成功,5个失败,可是实际观察只有4个成功,与设想有点偏差。试过用不一样的配置去测试,都一直存在这个问题,偏差为1,这里能够在之后研究为何出现这个问题。lua
总结
ngx_limit_req配置上更加灵活,不过测试中发现有一个缺点,就是与nginx的ngx_http_limit_req_module没有容许必定程度的并发。你们能够根据各自的使用场景决定用那种发发实现限流。
参考资料
https://github.com/openresty/lua-resty-limit-traffic
http://openresty.org/cn/components.html