一,前言
开发接口服务的过程中,为了防止客户端对于接口的滥用,保护服务器的资源, 通常来说我们会对于服务上的各种接口进行调用次数的限制。比如对于某个 用户,他在一个时间段(interval)内,比如 1 分钟,调用服务器接口的次数不能够 大于一个上限(limit),比如说 100 次。如果用户调用接口的次数超过上限的话,就直接拒绝用户的请求,返回错误信息。
二,令牌桶算法原理
随着时间流逝,系统会按恒定1/QPS时间间隔(如果QPS=100,则间隔是10ms)往桶里加入令牌,如果桶已经满了就不再加了.新请求来临时,会各自拿走一个令牌,如果没有令牌可拿了就阻塞或者拒绝服务.
三,基于redis实现的令牌桶算法
public function limit($uid = 0){
$redis = new \Redis();
$redis->connect('127.0.0.1', 6379);
//单个用户每分钟访问数
$initNum = 100;
$expire = 60;
$key = $uid . '_minNum';
$redis->watch($key);
$limitVal = $redis->get($key);
if ($limitVal) {
$limitVal = json_decode($limitVal, true);
$nowtime = time();
//计算当前时刻与上次访问的时间差乘以速率就是此次可以补充的令牌个数
$newNum = min($initNum, ($limitVal['num'] - 1) + (($initNum / $expire) * ($nowtime - $limitVal['time'])));
if ($newNum > 0) {
$redisVal = json_encode(['num' => $newNum, 'time' => time()]);
} else {
exit(json_encode(['status' => false, 'msg' => '当前时刻令牌消耗完!']));
}
} else {
//第一次访问时初始化令牌个数
$redisVal = json_encode(['num' => $initNum, 'time' => time()]);
}
$redis->multi();
$redis->set($key, $redisVal);
$result = $redis->exec();
if (!$result) {
exit(json_encode(['status' => false, 'msg' => '访问频次过多!']));
}
//其他操作}
redis的watch:?
-
watch 用于在进行事务操作的最后一步也就是在执行exec 之前对某个key进行监视 -
如果这个被监视的key被改动,那么事务就被取消,否则事务正常执行. -
一般在MULTI 命令前就用watch命令对某个key进行监控.如果想让key取消被监控,可以用unwatch命令
|