yii2自带的限流方式使用做在某路径下的,但是想要实现在某种规则下才限流,该怎么做呢?
(如果对限流的逻辑不明白,请搜索“漏斗算法”)
1. 对自带的限流工具做一下简单的修改,个人觉得,自带的限流算法有个小小的问题,在超限调用接口的时候,不应当记录次数
(library是我个人创建的放公共包的目录)
<?php namespace app\library; use yii\web\TooManyRequestsHttpException; class RateLimiter extends \yii\filters\RateLimiter { public function checkRateLimit($user, $request, $response, $action) { list($limit, $window) = $user->getRateLimit($request, $action); list($allowance, $timestamp) = $user->loadAllowance($request, $action); $current = time(); $allowance += (int) (($current - $timestamp) * $limit / $window); if ($allowance > $limit) { $allowance = $limit; } if ($allowance < 1) { //只注释了下面这一行代码,如果次数不足,则不更新时间 // $user->saveAllowance($request, $action, 0, $current); $this->addRateLimitHeaders($response, $limit, 0, $window); throw new TooManyRequestsHttpException($this->errorMessage); } $user->saveAllowance($request, $action, $allowance - 1, $current); $this->addRateLimitHeaders($response, $limit, $allowance - 1, (int) (($limit - $allowance + 1) * $window / $limit)); } }
2. 创建限流的对象(代码仅仅做个抛砖引玉,可以根据个人需要优化哈,也可以搞一个独立的限流工具)
$this->rateLimiter = Yii::createObject([ 'class' => \app\library\RateLimiter::class, 'errorMessage' => '您已经提交过,请勿重复提交',//超限提示 'enableRateLimitHeaders' => false,//是否把限流的详情展示到header中 'user' => new class implements RateLimitInterface {//实现限流对象 public function __construct() {//限流使用session,这里可以改成你想要的,也可以封装成store工厂 //提示,使用session实现的限流算法可能是不安全的(达不到限流的目的),请自行调整 $session = Yii::$app->getSession(); $session->open(); $this->store = $session; } public function getRateLimit($request, $action) { if ($action->id == 'xxxx') {//这里可以针对不同的action做到不同的限流方式 return [1, 300];//300秒之内只能执行一次 } return [2, 1];//1秒之内只能执行2次 } public function loadAllowance($request, $action) { return $this->get($action->id);//获取上一次的限流情况 } public function saveAllowance($request, $action, $allowance, $timestamp) { $this->save($action->id, $allowance, $timestamp);//保存本次的限流情况 } public function get($id)//返回的是[上一次的使用后剩余次数,上一次的更新时间] { return [$this->store[$id . '-allowance'], $this->store[$id . '-timestamp']]; } public function save($id, $allowance, $timestamp) {//保存上本次的限流详情 $this->store[$id . '-allowance'] = $allowance;//剩余次数 $this->store[$id . '-timestamp'] = $timestamp;//调用时间 } } ]);
3. 使用
//在你想要限流的地方加入下面的代码 $this->rateLimiter->checkRateLimit($this->rateLimiter->user, Yii::$app->request, Yii::$app->response, $this->action);