一、概念
频控:通过限制单位时间内可访问的次数,达到限流的目的。利用频控,可以用来限制访问某些服务接口的流量,防止服务器由于承受太大的访问量而崩溃。
二、实现
频控都会指定一个特征,比如:来自同一个用户的请求、来自同一个IP的请求等。实际应用场景中为防止服务过载,经常会有比如:每个用户在1分钟之内只能请求10次,每个IP在1小时之内只能请求30次等等这样的频控限制。一般都利用缓存比如Redis来实现频控。
比如限制每个用户(用$uid表示)在1分钟之内只允许有30次请求,那么我们首先想到Redis的实现方式是:
//第一步:以$uid为key,从0开始每次请求累加1进行计数
$count = redis->incr($uid);
//第二步:如果是第1次请求,那么设置该key的过期时间为60秒
if($count === 1){
redis->expire($uid,60);
}
//第三步:判断当前请求是否已超过该key所限制的30次
if($count > 30){
echo '已超过限制';
}
乍一看没什么问题,但如果在第二步时设置过期时间失败了,那么将导致一个周期(1分钟)之后,计数器不会被重置清零,而是一直累加,这样频控将是一直超限的。
为了确保在周期内,第1次累加和设置过期时间在一个事务中,可以利用SET方法。从Redis 2.6.12 开始,SET方法支持参数EX、PX、NX、XX。
//第一步:如果是第1次,设置过期时间后则会返回"OK",否则返回nil
$ret = redis->set($uid,1,array('EX'=>60,'NX'));
//第二步:如果不成功,说明本周期还未结束,计数累加
if(!$ret){
$count = redis->incr($uid);
//第三步:判断当前请求是否已超过该key所限制的30次
if($count > 30){
echo '已超过限制';
}
}