上篇學習了經過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