将如下代码加入控制器中
public function init()
{
# 限制每个用户 ip 访问抽奖接口频率
if (in_array(Yii::$app->requestedRoute, [
'index/riddle/test','index/riddle/draw-lottery'
])) {
$this->controllerLimit([
[
'funciton' => 'index/riddle/test',
#(10 秒钟只能访问 2 次)
'time_limit' => 10,
'try_times' => 2,
],
[
'funciton' => 'index/riddle/draw-lottery',
'time_limit' => 3600,
'try_times' => 15,
# 抽奖接口
]
], Yii::$app->requestedRoute);//限制 IP 访问接口次数
}
}
/**
* 请求次数限制
* @param $key
* @param $prefix //前缀,用于跟 key 组合存 cache
* @param $timeLimit //限制的时间
* @param $tryTimes //限制的次数
*/
public function tryLimit($key, $prefix, $timeLimit, $tryTimes)
{
# 此处使用 yii cache 使用 redis 效率更高
$cache = Yii::$app->cache;
$times = $cache->get($key . $prefix) ?: 0;
if ($times >= $tryTimes) {
return $this->_returnJson([], 429, "{$timeLimit} 秒钟只能请求 {$times} 次");
} else {
$cache->set($key . $prefix, $times + 1, $timeLimit);
}
}
/**
* 请求次数限制
* 通过 ip 进行限制
*/
public function controllerLimit($params, $funcName)
{
foreach ($params as $v) {
if ($v['funciton'] == $funcName) {
$this->tryLimit($v['funciton'], (string)Yii::$app->request->userIP, $v['time_limit'], $v['try_times']);
}
}
}
访问接口由正常到超过频次:
→ python3 jiekou.py
{
"data": [],
"error": 403,
"msg": "缺少 token 参数"
}
→ python3 jiekou.py
{
"data": [],
"error": 403,
"msg": "缺少 token 参数"
}
→ python3 jiekou.py
{
"data": [],
"error": 429,
"msg": "10 秒钟只能请求 2 次"
}
本例中使用 yii2 cache 存储键值对
替换成 redis 效率更高