PHP项目Symfony RateLimiter限流

wen PHP项目 2

本文目录导读:

PHP项目Symfony RateLimiter限流

  1. 安装RateLimiter组件
  2. 基本配置
  3. 在控制器中使用
  4. 高级用法
  5. 使用注解方式
  6. 自定义存储(Redis示例)
  7. 实用示例:API密钥限流
  8. 限流策略选择指南
  9. 注意事项

我会为您详细介绍如何在PHP的Symfony项目中使用RateLimiter(限流器)组件。

安装RateLimiter组件

composer require symfony/rate-limiter

基本配置

config/packages/rate_limiter.yaml 中配置:

framework:
    rate_limiter:
        # 定义限流器
        login_rate_limiter:
            # 存储策略
            policy: 'fixed_window'  # 固定窗口算法
            limit: 5               # 允许的请求数
            interval: '15 minutes' # 时间间隔
        api_rate_limiter:
            policy: 'sliding_window'  # 滑动窗口算法
            limit: 100
            interval: '1 hour'
        token_bucket_rate_limiter:
            policy: 'token_bucket'   # 令牌桶算法
            limit: 10                # 桶容量
            rate: 1                  # 令牌填充速率(个/秒)
            interval: '1 second'     # 填充间隔

在控制器中使用

基本使用方式:

<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\RateLimiter\RateLimiterFactory;
use Symfony\Component\RateLimiter\Exception\RateLimitExceededException;
class LoginController extends AbstractController
{
    private RateLimiterFactory $loginRateLimiter;
    public function __construct(RateLimiterFactory $loginRateLimiter)
    {
        $this->loginRateLimiter = $loginRateLimiter;
    }
    public function login(Request $request): Response
    {
        // 创建基于IP的限流器
        $limiter = $this->loginRateLimiter->create($request->getClientIp());
        try {
            // 消耗一个令牌
            $limiter->consume()->ensureAccepted();
            // 执行登录逻辑...
            return $this->json(['message' => '登录成功']);
        } catch (RateLimitExceededException $e) {
            // 返回限流信息
            $retryAfter = $e->getRetryAfter()->getTimestamp() - time();
            return $this->json([
                'error' => '请求过于频繁',
                'retry_after' => $retryAfter
            ], Response::HTTP_TOO_MANY_REQUESTS);
        }
    }
}

高级用法

获取限流状态信息:

<?php
public function apiEndpoint(Request $request): Response
{
    $limiter = $this->apiRateLimiter->create($request->getClientIp());
    // 消耗令牌并获取状态
    $limit = $limiter->consume();
    // 检查是否被限制
    if (!$limit->isAccepted()) {
        $headers = [
            'X-RateLimit-Remaining' => $limit->getRemainingTokens(),
            'X-RateLimit-Retry-After' => $limit->getRetryAfter()->getTimestamp(),
            'X-RateLimit-Limit' => $limit->getLimit(),
        ];
        return $this->json([
            'error' => 'Rate limit exceeded'
        ], Response::HTTP_TOO_MANY_REQUESTS, $headers);
    }
    $response = $this->json(['data' => '成功']);
    // 添加限流响应头
    $response->headers->set('X-RateLimit-Remaining', $limit->getRemainingTokens());
    $response->headers->set('X-RateLimit-Limit', $limit->getLimit());
    return $response;
}

使用注解方式

<?php
use Symfony\Component\RateLimiter\Annotation\RateLimiter;
class ApiController extends AbstractController
{
    /**
     * @RateLimiter(
     *     name="api_rate_limiter",
     *     key="request.getClientIp()",
     *     methods={"GET", "POST"}
     * )
     */
    public function protectedEndpoint(): Response
    {
        return $this->json(['data' => 'protected data']);
    }
}

自定义存储(Redis示例)

# config/packages/rate_limiter.yaml
framework:
    rate_limiter:
        storage:
            type: 'redis'
            dsn: 'redis://localhost:6379/0'
        login_rate_limiter:
            policy: 'fixed_window'
            limit: 5
            interval: '15 minutes'
            # 使用Redis存储
            storage: 'redis'

实用示例:API密钥限流

<?php
namespace App\Service;
use Symfony\Component\RateLimiter\RateLimiterFactory;
use Symfony\Component\HttpFoundation\Request;
class ApiRateLimiterService
{
    private array $rateLimiters;
    public function __construct(
        RateLimiterFactory $apiRateLimiter,
        RateLimiterFactory $tokenBucketRateLimiter
    ) {
        $this->rateLimiters = [
            'api' => $apiRateLimiter,
            'token_bucket' => $tokenBucketRateLimiter,
        ];
    }
    public function checkRateLimit(Request $request, string $type = 'api'): array
    {
        $key = $request->headers->get('X-API-Key') ?? $request->getClientIp();
        $limiter = $this->rateLimiters[$type]->create($key);
        $limit = $limiter->consume();
        return [
            'allowed' => $limit->isAccepted(),
            'remaining' => $limit->getRemainingTokens(),
            'retry_after' => $limit->getRetryAfter(),
            'headers' => [
                'X-RateLimit-Limit' => $limit->getLimit(),
                'X-RateLimit-Remaining' => $limit->getRemainingTokens(),
                'X-RateLimit-Reset' => $limit->getRetryAfter()->getTimestamp(),
            ]
        ];
    }
}

限流策略选择指南

  • 固定窗口(Fixed Window):适合简单的限流需求,可能在某时间点出现突刺
  • 滑动窗口(Sliding Window):更平滑的限流,适合API接口
  • 令牌桶(Token Bucket):允许突发流量,适合需要容忍短时间峰值的情况
  • 滑动日志(Sliding Log):最精确但资源消耗大

注意事项

  1. 选择合适的存储后端:高并发场景建议使用Redis
  2. 合理设置限流参数:根据业务需求调整limit和interval
  3. 返回适当的HTTP状态码:429 Too Many Requests
  4. 添加响应头:让客户端了解限流状态
  5. 考虑分布式环境:使用共享存储如Redis

这样就能在Symfony项目中有效地实现限流功能了。

抱歉,评论功能暂时关闭!