前言

每逢大促必压测,每逢大促必限流,这估计是电商人的常态。每次大促期间,业务流量是平时的几倍十几倍,大促期间大部分业务都会集中在购物车结算,必须限流,才能保证系统不宕机。

限流算法

限流算法一般有三种:令牌桶,漏桶,计数器。本文介绍最粗暴的计数器算法,其他算法请自行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
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117


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 sys_url_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,_=sys_url_limit:get(localkey);


        local req_key_s,_=sys_url_limit:get(key_s_limit);


        local req_key_m,_=sys_url_limit:get(key_m_limit);


        --每秒处理


        if req_key_s then


                 sys_url_limit:incr(key_s_limit,1)


                 if req_key_s>s then 


                    --return true 


               return false


                 end


       else


         sys_url_limit:set(key_s_limit,1,60)


       end


        --每分钟处理


        if req_key_m then


                 sys_url_limit:incr(key_m_limit,1)


                 if req_key_m>m then 


                    --return true


                    return false


                 end


        else


         sys_url_limit:set(key_m_limit,1,85)


        end


        return false


end

代码很简单,但是很实用,看一下调用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21


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