lua nginx openresty限流

上篇學習了經過Nginx模塊進行限流的方法,接下來學習一下利用Nginx+Lua進行接入層限流html

Openresty提供了lua-resty-limit-traffic模塊進行限流,模塊實現了limit.connlimit.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

https://docs.konghq.com/install/source/