yii2 实现灵活的限流方式

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);
此条目发表在php, yii2分类目录,贴了, , 标签。将固定链接加入收藏夹。