2019-05-15 11:12:49 +08:00
|
|
|
|
# 令牌桶限流器
|
|
|
|
|
|
2019-12-12 16:24:04 +08:00
|
|
|
|
## 安裝
|
2019-05-15 11:12:49 +08:00
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
composer require hyperf/rate-limit
|
|
|
|
|
```
|
2021-02-22 08:56:22 +08:00
|
|
|
|
|
|
|
|
|
## 配置
|
|
|
|
|
|
|
|
|
|
### 發佈配置
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
php bin/hyperf.php vendor:publish hyperf/rate-limit
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 配置説明
|
2019-05-15 11:12:49 +08:00
|
|
|
|
|
2022-08-17 18:28:00 +08:00
|
|
|
|
| 配置 | 默認值 | 類型 | 備註 |
|
|
|
|
|
|:--------------:|:------:|:--------------:|:-------------------:|
|
|
|
|
|
| create | 1 |int| 每秒生成令牌數 |
|
|
|
|
|
| consume | 1 |int| 每次請求消耗令牌數 |
|
|
|
|
|
| capacity | 2 |int| 令牌桶最大容量 |
|
|
|
|
|
| limitCallback | `[]` |null\|callable| 觸發限流時回調方法 |
|
|
|
|
|
| waitTimeout | 1 |int| 排隊超時時間 |
|
|
|
|
|
| key | 當前請求 url 地址 |callable\|string| 限流的 key |
|
2019-05-15 11:12:49 +08:00
|
|
|
|
|
|
|
|
|
## 使用限流器
|
|
|
|
|
|
2019-12-12 16:24:04 +08:00
|
|
|
|
組件提供 `Hyperf\RateLimit\Annotation\RateLimit` 註解,作用於類、類方法,可以覆蓋配置文件。 例如,
|
2019-05-15 11:12:49 +08:00
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Controller;
|
|
|
|
|
|
|
|
|
|
use Hyperf\HttpServer\Annotation\Controller;
|
|
|
|
|
use Hyperf\HttpServer\Annotation\RequestMapping;
|
|
|
|
|
use Hyperf\RateLimit\Annotation\RateLimit;
|
|
|
|
|
|
2021-12-01 16:19:47 +08:00
|
|
|
|
#[Controller(prefix: "rate-limit")]
|
2019-05-15 11:12:49 +08:00
|
|
|
|
class RateLimitController
|
|
|
|
|
{
|
2021-12-01 16:19:47 +08:00
|
|
|
|
#[RequestMapping(path: "test")]
|
|
|
|
|
#[RateLimit(create: 1, capacity: 3)]
|
2019-05-15 11:12:49 +08:00
|
|
|
|
public function test()
|
|
|
|
|
{
|
2019-12-12 16:24:04 +08:00
|
|
|
|
return ["QPS 1, 峯值3"];
|
2019-05-15 11:12:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
2021-12-01 16:19:47 +08:00
|
|
|
|
#[RequestMapping(path: "test2")]
|
|
|
|
|
#[RateLimit(create: 2, consume: 2, capacity: 4)]
|
2019-05-15 11:12:49 +08:00
|
|
|
|
public function test2()
|
|
|
|
|
{
|
2019-12-12 16:24:04 +08:00
|
|
|
|
return ["QPS 2, 峯值2"];
|
2019-05-15 11:12:49 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
2019-12-12 16:24:04 +08:00
|
|
|
|
配置優先級 `方法註解 > 類註解 > 配置文件 > 默認配置`
|
2019-05-15 11:12:49 +08:00
|
|
|
|
|
2019-12-12 16:24:04 +08:00
|
|
|
|
## 觸發限流
|
|
|
|
|
當限流被觸發時, 默認會拋出 `Hyperf\RateLimit\Exception\RateLimitException` 異常
|
2019-05-15 11:12:49 +08:00
|
|
|
|
|
2019-12-12 16:41:17 +08:00
|
|
|
|
可以通過[異常處理](zh-hk/exception-handler.md)或者配置 `limitCallback` 限流回調處理。
|
2019-06-20 11:31:26 +08:00
|
|
|
|
|
|
|
|
|
例如:
|
2019-05-15 11:12:49 +08:00
|
|
|
|
```php
|
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Controller;
|
|
|
|
|
|
|
|
|
|
use Hyperf\Di\Aop\ProceedingJoinPoint;
|
|
|
|
|
use Hyperf\HttpServer\Annotation\Controller;
|
|
|
|
|
use Hyperf\HttpServer\Annotation\RequestMapping;
|
|
|
|
|
use Hyperf\RateLimit\Annotation\RateLimit;
|
|
|
|
|
|
2021-12-01 16:19:47 +08:00
|
|
|
|
#[Controller(prefix: "rate-limit")]
|
|
|
|
|
#[RateLimit(limitCallback: [RateLimitController::class, "limitCallback"])]
|
2019-05-15 11:12:49 +08:00
|
|
|
|
class RateLimitController
|
|
|
|
|
{
|
2021-12-01 16:19:47 +08:00
|
|
|
|
#[RequestMapping(path: "test")]
|
|
|
|
|
#[RateLimit(create: 1, capacity: 3)]
|
2019-05-15 11:12:49 +08:00
|
|
|
|
public function test()
|
|
|
|
|
{
|
2019-12-12 16:24:04 +08:00
|
|
|
|
return ["QPS 1, 峯值3"];
|
2019-05-15 11:12:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-20 11:31:26 +08:00
|
|
|
|
public static function limitCallback(float $seconds, ProceedingJoinPoint $proceedingJoinPoint)
|
2019-05-15 11:12:49 +08:00
|
|
|
|
{
|
2019-12-12 16:24:04 +08:00
|
|
|
|
// $seconds 下次生成Token 的間隔, 單位為秒
|
|
|
|
|
// $proceedingJoinPoint 此次請求執行的切入點
|
2021-04-18 22:51:00 +08:00
|
|
|
|
// 可以通過調用 `$proceedingJoinPoint->process()` 繼續完成執行,或者自行處理
|
2019-06-20 11:31:26 +08:00
|
|
|
|
return $proceedingJoinPoint->process();
|
2019-05-15 11:12:49 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-08-28 11:42:06 +08:00
|
|
|
|
```
|
2022-08-17 18:28:00 +08:00
|
|
|
|
|
|
|
|
|
## 自定義令牌桶限流 key
|
|
|
|
|
|
|
|
|
|
默認的 key 是根據當前請求的 `url` ,當一個用户觸發限流時,其他用户也被限流請求此`url`;
|
|
|
|
|
|
|
|
|
|
若需要不同顆粒度的限流, 如用户緯度的限流,可以針對用户 `ID` 進行限流,達到 A 用户被限流,B 用户正常請求:
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace App\Controller;
|
|
|
|
|
|
|
|
|
|
use Hyperf\Di\Aop\ProceedingJoinPoint;
|
|
|
|
|
use Hyperf\RateLimit\Annotation\RateLimit;
|
2023-04-11 15:57:32 +08:00
|
|
|
|
use Hyperf\Context\ApplicationContext;
|
2022-08-17 18:28:00 +08:00
|
|
|
|
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');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
```
|