将如下代码加入控制器中
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 效率更高