本文主要是介绍大型商城活动防刷限流方案,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
最近负责的一个某品牌手机的官方商城,他们要发售一款新手机,以往都是各个渠道一起发售,但是本次决定官网首发10000台,这样一来其他渠道的消费者都会被引流到官网来(天猫/京东/苏宁/线下),其庞大的流量并发可想而知,原有的功能实现肯定无法承载这种体量,因此我们全面优化了预售功能,分别按照以下几个点来操作:
1. 页面静态化(动态数据全部通过js异步获取,并且需要控制异步请求的数量,页面缓存到CDN)
2.防bot(包括防刷、限流等)
今天主讲一下发刷和限流方案,需要用到的技术包括Nginx+Lua+Redis
OK我们先来看一下防刷代码,看完代码再来讲解这段代码:
-- access_by_lua_file '/opt/ops/lua/access_limit.lua'
local function close_redis(red) if not red then return end --释放连接(连接池实现) local pool_max_idle_time = 10000 --毫秒 local pool_size = 100 --连接池大小 local ok, err = red:set_keepalive(pool_max_idle_time, pool_size) if not ok then ngx_log(ngx_ERR, "set redis keepalive error : ", err) end
end local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000)
local ip = "redis-ip"
local port = redis-port
local ok, err = red:connect(ip,port)
if not ok then return close_redis(red)
end local clientIP = ngx.req.get_headers()["X-Real-IP"]
if clientIP == nil then clientIP = ngx.req.get_headers()["x_forwarded_for"]
end
if clientIP == nil then clientIP = ngx.var.remote_addr
end local incrKey = "user:"..clientIP..":freq"
local blockKey = "user:"..clientIP..":block" local is_block,err = red:get(blockKey) -- check if ip is blocked
if tonumber(is_block) == 1 then ngx.exit(ngx.HTTP_FORBIDDEN) return close_redis(red)
end res, err = red:incr(incrKey) if res == 1 then res, err = red:expire(incrKey,1)
end if res > 200 then res, err = red:set(blockKey,1) res, err = red:expire(blockKey,600)
end close_redis(red)
这段的逻辑是这样,请求过来的时候获取当前访问者的ip,拼接出两个key,一个是用于统计该ip的请求次数的,另一个是用来标识是否被限制了,然后redis里去先去get一下这个ip是否被限制了,如果返回1标识被限制了,直接nginx返回403,否则的话对当前ip进行计数,第一次计数时,计数完毕后设置该计数器的失效时间1秒,后面判断计数器的大小是否查过了200,也就是说,如果在1秒的失效时间内请求了超过200次,那么设置当前ip为受限,并设置受限时间为10分钟。
再来看看限流代码,看完代码再来讲解这段代码:
-- access_by_lua_file '/opt/ops/lua/access_flow_control.lua'
local function close_redis(red) if not red then return end --释放连接(连接池实现) local pool_max_idle_time = 10000 --毫秒 local pool_size = 100 --连接池大小 local ok, err = red:set_keepalive(pool_max_idle_time, pool_size) if not ok then ngx_log(ngx_ERR, "set redis keepalive error : ", err) end
end local function wait() ngx.sleep(1)
end local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000)
local ip = "redis-ip"
local port = redis-port
local ok, err = red:connect(ip,port)
if not ok then return close_redis(red)
end local uri = ngx.var.uri -- 获取当前请求的uri
local uriKey = "req:uri:"..uri
res, err = red:eval("local res, err = redis.call('incr',KEYS[1]) if res == 1 then local resexpire, err = redis.call('expire',KEYS[1],KEYS[2]) end return (res)",2,uriKey,1)
while (res > 10)
do local twait, err = ngx.thread.spawn(wait) ok, threadres = ngx.thread.wait(twait) if not ok then ngx_log(ngx_ERR, "wait sleep error: ", err) break; end res, err = red:eval("local res, err = redis.call('incr',KEYS[1]) if res == 1 then local resexpire, err = redis.call('expire',KEYS[1],KEYS[2]) end return (res)",2,uriKey,1)
end
close_redis(red)
限流的逻辑是这样的请求过来获取你的url然后以这个url作为key去redis里计数,然后判断当前url被请求过多少次,如果是一次那么设置该key的失效时间是1秒,然后有一个while循环,循环的条件是当前url被请求的次数大于10,里面做的事情是让当前这个请求等待1秒,此时前面设置的失效时间应该到了,然后再去对当前url计一次数此时返回的数量理论上市1,如果数量等于1那么设置失效时间为1,然后继续while的循环,如果大于10就让他一直去循环做等待1秒然后设置请求次数加1,如果不大于1秒那么就放行允许请求去访问我们的tomcat,所以这里的逻辑其实限制了并发的请求数量是10,也就是同一时刻只允许10个请求通过校验走到tomcat,后面的请求都在排队等待。
说白了就是10个请求进来后,然后让后面的请求等待1秒然后再放进来10个一直这样循环处理。
ok以上就是我分享的防刷和限流解决方案,lua脚本是网上找来的,仅供参考。
这篇关于大型商城活动防刷限流方案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!