前言
每逢大促必壓測,每逢大促必限流,這估計是電商人的常態。每次大促期間,業務流量是平時的幾倍十幾倍,大促期間大部分業務都會集中在購物車結算,必須限流,才能保證系統不宕機。
限流算法
限流算法一般有三種:令牌桶,漏桶,計數器。本文介紹最粗暴的計數器算法,其他算法請自行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中配置。