From e63a399d0d86fc0e8f4372438821f4cf73b88996 Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Tue, 18 Nov 2025 14:45:21 +0800 Subject: [PATCH 1/4] feat: add SmartOrder annotation for intelligent RateLimit prioritization This feature introduces a new SmartOrder annotation that enables intelligent prioritization of multiple RateLimit annotations on the same method. Key changes: - Added SmartOrder annotation class for marking methods that need smart ordering - Modified RateLimitAspect to support priority-based RateLimit processing - RateLimits are now processed in order of strictness (maxAttempts/decay ratio) - More restrictive rate limits are evaluated first for better efficiency The SmartOrder annotation allows developers to optimize rate limiting behavior by ensuring stricter limits are checked before more lenient ones. --- src/rate-limit/src/Annotation/SmartOrder.php | 20 +++++++++++++++++++ src/rate-limit/src/Aspect/RateLimitAspect.php | 14 +++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 src/rate-limit/src/Annotation/SmartOrder.php diff --git a/src/rate-limit/src/Annotation/SmartOrder.php b/src/rate-limit/src/Annotation/SmartOrder.php new file mode 100644 index 000000000..073df3fee --- /dev/null +++ b/src/rate-limit/src/Annotation/SmartOrder.php @@ -0,0 +1,20 @@ +method[RateLimit::class] ?? null; + $isSmartOrder = $metadata->method[SmartOrder::class] ?? false; + /** @var SplPriorityQueue $queue */ + $queue = new SplPriorityQueue(); foreach ($annotations?->toAnnotations() ?? [] as $annotation) { + /** @var RateLimit $annotation */ + $priority = 0; + if ($isSmartOrder) { + $priority = 0 - $annotation->maxAttempts / $annotation->decay; + } + $queue->insert($annotation, $priority); + } + + foreach ($queue as $annotation) { /** @var RateLimit $annotation */ $key = $this->resolveKey($annotation->key, $proceedingJoinPoint); $limiter = $this->factory->make($annotation->algorithm, $annotation->pool); From 870adc99f8ef0755cb53ab843130c69dc752270b Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Tue, 18 Nov 2025 14:50:32 +0800 Subject: [PATCH 2/4] feat: replace SmartOrder annotation with AutoSort for improved rate limit prioritization --- .../src/Annotation/{SmartOrder.php => AutoSort.php} | 2 +- src/rate-limit/src/Aspect/RateLimitAspect.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) rename src/rate-limit/src/Annotation/{SmartOrder.php => AutoSort.php} (90%) diff --git a/src/rate-limit/src/Annotation/SmartOrder.php b/src/rate-limit/src/Annotation/AutoSort.php similarity index 90% rename from src/rate-limit/src/Annotation/SmartOrder.php rename to src/rate-limit/src/Annotation/AutoSort.php index 073df3fee..7cb85f163 100644 --- a/src/rate-limit/src/Annotation/SmartOrder.php +++ b/src/rate-limit/src/Annotation/AutoSort.php @@ -15,6 +15,6 @@ use Hyperf\Di\Annotation\AbstractAnnotation; #[Attribute(Attribute::TARGET_METHOD)] -class SmartOrder extends AbstractAnnotation +class AutoSort extends AbstractAnnotation { } diff --git a/src/rate-limit/src/Aspect/RateLimitAspect.php b/src/rate-limit/src/Aspect/RateLimitAspect.php index 2f2cbd95f..f3ffcf800 100644 --- a/src/rate-limit/src/Aspect/RateLimitAspect.php +++ b/src/rate-limit/src/Aspect/RateLimitAspect.php @@ -11,8 +11,8 @@ namespace FriendsOfHyperf\RateLimit\Aspect; +use FriendsOfHyperf\RateLimit\Annotation\AutoSort; use FriendsOfHyperf\RateLimit\Annotation\RateLimit; -use FriendsOfHyperf\RateLimit\Annotation\SmartOrder; use FriendsOfHyperf\RateLimit\Exception\RateLimitException; use FriendsOfHyperf\RateLimit\RateLimiterFactory; use Hyperf\Di\Annotation\MultipleAnnotation; @@ -39,14 +39,14 @@ public function process(ProceedingJoinPoint $proceedingJoinPoint) /** @var null|MultipleAnnotation $annotations */ $annotations = $metadata->method[RateLimit::class] ?? null; - $isSmartOrder = $metadata->method[SmartOrder::class] ?? false; + $isAutoSort = $metadata->method[AutoSort::class] ?? false; /** @var SplPriorityQueue $queue */ $queue = new SplPriorityQueue(); foreach ($annotations?->toAnnotations() ?? [] as $annotation) { /** @var RateLimit $annotation */ $priority = 0; - if ($isSmartOrder) { + if ($isAutoSort) { $priority = 0 - $annotation->maxAttempts / $annotation->decay; } $queue->insert($annotation, $priority); From fbf2a3a86429b8df52e4ae9783c4119bc3d2c152 Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Tue, 18 Nov 2025 14:55:30 +0800 Subject: [PATCH 3/4] Update README.md Co-Authored-By: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-Authored-By: huangdijia <8337659+huangdijia@users.noreply.github.com> --- src/rate-limit/README.md | 71 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/src/rate-limit/README.md b/src/rate-limit/README.md index f4060b77b..4e8f51c27 100644 --- a/src/rate-limit/README.md +++ b/src/rate-limit/README.md @@ -27,6 +27,10 @@ composer require friendsofhyperf/rate-limit - **Flexible Usage** - Annotation-based rate limiting via Aspect - Custom middleware support +- **Smart Order for Multiple Annotations** + - Automatic prioritization of multiple RateLimit annotations + - Intelligent ordering by strictness (maxAttempts/decay ratio) + - More restrictive limits evaluated first for better performance - **Flexible Key Generation** - Default method/class-based keys - Custom key with placeholders support @@ -39,7 +43,7 @@ composer require friendsofhyperf/rate-limit ## Usage -### Method 1: Using Annotagion +### Method 1: Using Annotation The easiest way to add rate limiting is using the `#[RateLimit]` attribute: @@ -142,6 +146,49 @@ class UserController | `response` | `string` | `'Too Many Attempts.'` | Custom response when rate limit is exceeded | | `responseCode` | `int` | `429` | HTTP response code when rate limit is exceeded | +#### Multiple RateLimits with AutoSort + +When you need multiple rate limits on the same method (e.g., per-minute and per-hour limits), you can use the `AutoSort` annotation to automatically prioritize them: + +```php + Date: Tue, 18 Nov 2025 14:59:38 +0800 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E4=B8=AD?= =?UTF-8?q?=E6=96=87=E6=96=87=E6=A1=A3=E4=BB=A5=E6=94=AF=E6=8C=81=E9=99=90?= =?UTF-8?q?=E6=B5=81=E7=BB=84=E4=BB=B6=E7=9A=84=E4=BD=BF=E7=94=A8=E8=AF=B4?= =?UTF-8?q?=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rate-limit/README_CN.md | 474 ++++++++++++++++++++++++++++++++++++ 1 file changed, 474 insertions(+) create mode 100644 src/rate-limit/README_CN.md diff --git a/src/rate-limit/README_CN.md b/src/rate-limit/README_CN.md new file mode 100644 index 000000000..465f34973 --- /dev/null +++ b/src/rate-limit/README_CN.md @@ -0,0 +1,474 @@ +# Rate Limit + +[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/rate-limit/v)](https://packagist.org/packages/friendsofhyperf/rate-limit) +[![Total Downloads](https://poser.pugx.org/friendsofhyperf/rate-limit/downloads)](https://packagist.org/packages/friendsofhyperf/rate-limit) +[![License](https://poser.pugx.org/friendsofhyperf/rate-limit/license)](https://packagist.org/packages/friendsofhyperf/rate-limit) + +Hyperf 的限流组件,支持多种算法(固定窗口、滑动窗口、令牌桶、漏桶)。 + +## 安装 + +```bash +composer require friendsofhyperf/rate-limit +``` + +## 环境要求 + +- Hyperf ~3.1.0 +- Redis + +## 特性 + +- **多种限流算法** + - 固定窗口 + - 滑动窗口 + - 令牌桶 + - 漏桶 +- **灵活的使用方式** + - 基于注解的限流(通过切面实现) + - 自定义中间件支持 +- **多注解智能排序** + - 自动对多个 RateLimit 注解进行优先级排序 + - 根据严格程度智能排序(maxAttempts/decay 比率) + - 更严格的限制优先检查,提升性能 +- **灵活的键生成** + - 默认基于方法/类的键 + - 支持自定义键和占位符 + - 支持数组键 + - 支持可调用键 +- **自定义响应** + - 自定义响应消息 + - 自定义 HTTP 响应码 +- **多 Redis 连接池支持** + +## 使用方式 + +### 方式一:使用注解 + +最简单的方式是使用 `#[RateLimit]` 属性: + +```php +getClientIp(); + } +} +``` + +然后在配置中注册中间件: + +```php +// config/autoload/middlewares.php +return [ + 'http' => [ + App\Middleware\ApiRateLimitMiddleware::class, + ], +]; +``` + +### 限流算法 + +#### 固定窗口(默认) + +最简单的算法,在固定时间窗口内计数请求。 + +```php +#[RateLimit(algorithm: Algorithm::FIXED_WINDOW)] +``` + +**优点**:简单,内存高效 +**缺点**:可能在窗口边界处允许突发请求 + +#### 滑动窗口 + +比固定窗口更准确,均匀分布请求。 + +```php +#[RateLimit(algorithm: Algorithm::SLIDING_WINDOW)] +``` + +**优点**:平滑突发流量,更准确 +**缺点**:稍微复杂一些 + +#### 令牌桶 + +允许突发流量,同时保持平均速率。 + +```php +#[RateLimit(algorithm: Algorithm::TOKEN_BUCKET)] +``` + +**优点**:允许突发流量,灵活 +**缺点**:需要更多配置 + +#### 漏桶 + +以恒定速率处理请求,排队突发流量。 + +```php +#[RateLimit(algorithm: Algorithm::LEAKY_BUCKET)] +``` + +**优点**:平滑输出速率,防止突发 +**缺点**:可能延迟请求 + +### 自定义限流器 + +你可以通过实现 `RateLimiterInterface` 来实现自己的限流器: + +```php +index(); +} catch (FriendsOfHyperf\RateLimit\Exception\RateLimitException $e) { + // 超出限流 + $message = $e->getMessage(); // "Too Many Attempts. Please try again in X seconds." + $code = $e->getCode(); // 429 +} +``` + +## 配置 + +组件使用 Hyperf 的 Redis 配置。你可以在注解或中间件中指定使用的 Redis 连接池: + +```php +// 使用特定的 Redis 连接池 +#[RateLimit(pool: 'rate_limit')] +``` + +确保在 `config/autoload/redis.php` 中配置 Redis 连接池: + +```php +return [ + 'default' => [ + 'host' => env('REDIS_HOST', 'localhost'), + 'port' => env('REDIS_PORT', 6379), + 'auth' => env('REDIS_AUTH', null), + 'db' => 0, + 'pool' => [ + 'min_connections' => 1, + 'max_connections' => 30, + ], + ], + 'rate_limit' => [ + 'host' => env('REDIS_HOST', 'localhost'), + 'port' => env('REDIS_PORT', 6379), + 'auth' => env('REDIS_AUTH', null), + 'db' => 1, + 'pool' => [ + 'min_connections' => 5, + 'max_connections' => 50, + ], + ], +]; +``` + +## 示例 + +### 示例 1:登录限流 + +限制登录尝试以防止暴力破解: + +```php +#[RateLimit( + key: 'login:{email}', + maxAttempts: 5, + decay: 300, // 5 分钟 + response: 'Too many login attempts. Please try again after 5 minutes.', + responseCode: 429 +)] +public function login(string $email, string $password) +{ + // 登录逻辑 +} +``` + +### 示例 2:API 端点限流 + +为不同的 API 端点设置不同的限流: + +```php +class ApiController +{ + // 公共 API:每分钟 100 次请求 + #[RateLimit(maxAttempts: 100, decay: 60)] + public function public() + { + // 公共端点 + } + + // 高级 API:每分钟 1000 次请求 + #[RateLimit(maxAttempts: 1000, decay: 60)] + public function premium() + { + // 高级端点 + } +} +``` + +### 示例 3:基于用户的限流 + +按用户限流: + +```php +#[RateLimit( + key: ['user', '{userId}', 'action'], + maxAttempts: 10, + decay: 3600 // 1 小时 +)] +public function performAction(int $userId) +{ + // 操作逻辑 +} +``` + +### 示例 4:基于 IP 的限流 + +使用中间件按 IP 地址限流: + +```php +class IpRateLimitMiddleware extends RateLimitMiddleware +{ + protected function resolveKey(ServerRequestInterface $request): string + { + return 'ip:' . $this->getClientIp(); + } +} +``` + +### 示例 5:使用 AutoSort 的多级限流 + +使用 AutoSort 高效处理昂贵操作的多级限流: + +```php +class ReportController +{ + /** + * 昂贵的报告生成,多级保护 + * AutoSort 确保优先检查严格的限制,提升性能 + */ + #[AutoSort] + #[RateLimit(maxAttempts: 5, decay: 60, response: 'Too many requests. Max 5 per minute')] // 紧急制动 + #[RateLimit(maxAttempts: 30, decay: 3600, response: 'Hourly limit exceeded. Max 30 per hour')] // 持续负载 + #[RateLimit(maxAttempts: 100, decay: 86400, response: 'Daily limit exceeded. Max 100 per day')] // 每日上限 + public function generateReport($reportType) + { + // 昂贵的报告生成逻辑 + } +} +``` + +## 许可证 + +[MIT](LICENSE)