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);