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中配置。