大致流程:
-
每次请求就写入有序集合里面,集合的sorce值是当前毫秒时间戳(防止秒出现重复),可以认为每一次请求就一个时间戳在里面。
-
从集合里面去掉10分钟以前所有的集合数据。然后计算出当前的集合里面数量
-
根据这个数量来与我们阈值做大小判断,如果超过就锁住,否则继续走下去
//将我们时间戳写入我们redis的有序集合里面
Redis::zadd('user:1:request:nums',1561456435,'1561456435.122');
//设置key的过期时间为10分钟
Redis::expire('user:1:request:nums',300);
//删除我们10分钟以前的数据
Redis::ZREMRANGEBYSCORE('user:1:request:nums',0,1561456135);
//获取里面剩下请求个数
$request_nums=(int)Redis::zcard(self::TIMELINE_ELEVEL_KEY);
if($request_nums >= 10){
//加入小黑屋,下次再进来就要锁定判断
}
...
private final static String TEXT="local ratelimit_info = redis.pcall('HMGET',KEYS[1],'last_time','current_token')\n" +
"local last_time = ratelimit_info[1]\n" +
"local current_token = tonumber(ratelimit_info[2])\n" +
"local max_token = tonumber(ARGV[1])\n" +
"local token_rate = tonumber(ARGV[2])\n" +
"local current_time = tonumber(ARGV[3])\n" +
"if current_token == nil then\n" +
" current_token = max_token\n" +
" last_time = current_time\n" +
"else\n" +
" local past_time = current_time-last_time\n" +
" \n" +
" if past_time>1000 then\n" +
"\t current_token = current_token+token_rate\n" +
"\t last_time = current_time\n" +
" end\n" +
"\n" +
" if current_token>max_token then\n" +
" current_token = max_token\n" +
"\tlast_time = current_time\n" +
" end\n" +
"end\n" +
"\n" +
"local result = 0\n" +
"if(current_token>0) then\n" +
" result = 1\n" +
" current_token = current_token-1\n" +
" last_time = current_time\n" +
"end\n" +
"redis.call('HMSET',KEYS[1],'last_time',last_time,'current_token',current_token)\n" +
"return result";