hyperf/docs/zh-hk/rate-limit.md
2022-08-17 18:28:00 +08:00

3.6 KiB
Raw Blame History

令牌桶限流器

安裝

composer require hyperf/rate-limit

配置

發佈配置

php bin/hyperf.php vendor:publish hyperf/rate-limit

配置説明

配置 默認值 類型 備註
create 1 int 每秒生成令牌數
consume 1 int 每次請求消耗令牌數
capacity 2 int 令牌桶最大容量
limitCallback [] null|callable 觸發限流時回調方法
waitTimeout 1 int 排隊超時時間
key 當前請求 url 地址 callable|string 限流的 key

使用限流器

組件提供 Hyperf\RateLimit\Annotation\RateLimit 註解,作用於類、類方法,可以覆蓋配置文件。 例如,

<?php

namespace App\Controller;

use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\RequestMapping;
use Hyperf\RateLimit\Annotation\RateLimit;

#[Controller(prefix: "rate-limit")]
class RateLimitController
{
    #[RequestMapping(path: "test")]
    #[RateLimit(create: 1, capacity: 3)]
    public function test()
    {
        return ["QPS 1, 峯值3"];
    }

    #[RequestMapping(path: "test2")]
    #[RateLimit(create: 2, consume: 2, capacity: 4)]
    public function test2()
    {
        return ["QPS 2, 峯值2"];
    }
}

配置優先級 方法註解 > 類註解 > 配置文件 > 默認配置

觸發限流

當限流被觸發時, 默認會拋出 Hyperf\RateLimit\Exception\RateLimitException 異常

可以通過異常處理或者配置 limitCallback 限流回調處理。

例如:

<?php

namespace App\Controller;

use Hyperf\Di\Aop\ProceedingJoinPoint;
use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\RequestMapping;
use Hyperf\RateLimit\Annotation\RateLimit;

#[Controller(prefix: "rate-limit")]
#[RateLimit(limitCallback: [RateLimitController::class, "limitCallback"])]
class RateLimitController
{
    #[RequestMapping(path: "test")]
    #[RateLimit(create: 1, capacity: 3)]
    public function test()
    {
        return ["QPS 1, 峯值3"];
    }
    
    public static function limitCallback(float $seconds, ProceedingJoinPoint $proceedingJoinPoint)
    {
        // $seconds 下次生成Token 的間隔, 單位為秒
        // $proceedingJoinPoint 此次請求執行的切入點
        // 可以通過調用 `$proceedingJoinPoint->process()` 繼續完成執行,或者自行處理
        return $proceedingJoinPoint->process();
    }
}

自定義令牌桶限流 key

默認的 key 是根據當前請求的 url ,當一個用户觸發限流時,其他用户也被限流請求此url

若需要不同顆粒度的限流, 如用户緯度的限流,可以針對用户 ID 進行限流,達到 A 用户被限流B 用户正常請求:

<?php

declare(strict_types=1);


namespace App\Controller;

use Hyperf\Di\Aop\ProceedingJoinPoint;
use Hyperf\RateLimit\Annotation\RateLimit;
use Hyperf\Utils\ApplicationContext;
use Hyperf\HttpServer\Contract\RequestInterface;

class TestController
{
    /**
     * @RateLimit(create=1, capacity=3, key={TestController::class, "getUserId"})
     */
    public function test(TestRequest $request)
    {

        return ["QPS 1, 峯值3"];
    }

    public static function getUserId(ProceedingJoinPoint $proceedingJoinPoint)
    {
        $request = ApplicationContext::getContainer()->get(RequestInterface::class);
        // 同理可以根據手機號、IP地址等不同緯度進行限流
        return $request->input('user_id');
    }

}