Nginx + LUA下流量攔截算法


前言

每逢大促必壓測,每逢大促必限流,這估計是電商人的常態。每次大促期間,業務流量是平時的幾倍十幾倍,大促期間大部分業務都會集中在購物車結算,必須限流,才能保證系統不宕機。

限流算法

限流算法一般有三種:令牌桶,漏桶,計數器。本文介紹最粗暴的計數器算法,其他算法請自行google、百度,講的應該比我好。(能解決問題的算法都是好算法)

lua 限流

業務結構

在大促期間由於流量過高,現有服務器無法承受那么大的流量,租用雲服務是很好的選擇。

業務架構圖可以看出 ,我們的服務器有自有服務器,首都在線雲,金山雲。自有服務器由於采購時間原因,每個批次的服務器性能不是很一致,首都在線雲、金山雲的服務器性能又有很大的差別,

lvs在權重設置上也有很大的不同,這對我們使用lua限流又造成了一定的困擾。當然這只是小問題,既然lvs只是權重,那么我們的lua限流也支持權重即可。

nginx+lua

使用nginx+lua的原因很簡單,我們服務架構nginx+fastcgi+php,為了防止php進程被打滿,我們只需要在nginx加一層限制,就可以簡單粗暴的解決問題,而lua正好滿足這個條件。業務場景的不同,我們選擇的算法也不同,越是復雜的東西,越希望最簡單話的解決方案。引入越多的東西,就會造成更多的不確定性。

話不多說上代

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
ngx.header.content_type =  "text/html; charset=utf-8" ;
local method=ngx.req.get_method()
local curl=ngx.md5(ngx. var .request_uri);
local request_uri_without_args = ngx.re.sub(ngx. var .request_uri,  "\\?.*" ,  "" );
local match = string.match
local ngxmatch=ngx.re.match
 
 
--限流計數
function limit_url_check(key,s,m)
        local localkey=key;
        local yyy_limit=ngx.shared.url_limit
        --每分鍾限制
         local key_m_limit=  localkey..os.date( "%Y-%m-%d %H:%M" , ngx.time()) 
             --每秒限制
         local key_s_limit=  localkey..os.date( "%Y-%m-%d %H:%M:%S" , ngx.time())
         local req_key,_=yyy_limit:get(localkey);
         local req_key_s,_=yyy_limit:get(key_s_limit);
         local req_key_m,_=yyy_limit:get(key_m_limit);
         --每秒處理
         if  req_key_s then
                  yyy_limit:incr(key_s_limit,1)
                  if  req_key_s>s then 
                     -- return  true 
                return  false
                  end
         else
          yyy_limit:set(key_s_limit,1,60)
        end
        --每分鍾處理
         if  req_key_m then
                  yyy_limit:incr(key_m_limit,1)
                  if  req_key_m>m then 
                     -- return  true
                     return  false
                  end
         else
          yyy_limit:set(key_m_limit,1,85)
        end
         return  false
end

代碼很簡單,但是很實用,看一下調用

1
2
3
4
5
6
7
if    ngx.re.match(request_uri_without_args,"/cart/inf(.*)") then
       if  limit_url_check("appcartinfo",24,3200) then
          ngx.say( '{"code": 524,"msg": "啊啊啊,每逢大促,排隊結賬。當前訪問的用戶過多,請稍后再試!","alert": "啊啊啊,每逢大促,排隊結賬。當前訪問的用戶過多,請稍后再試!"}' )
          ngx.exit(200);
                 return
      end
end

調用的代碼秒,分鍾的限制數可以放到cjson中配置。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM