PHP/Symfony中如何实现每分钟请求达20次后设置请求延迟?
Absolutely! Both vanilla PHP and Symfony provide straightforward ways to implement rate limiting that adds delays once you hit your 20 requests-per-minute threshold. Let's walk through both approaches.
Vanilla PHP Implementation
The core idea is to track request counts per minute, then calculate and apply delays once you exceed your limit. You'll need a shared storage system (like Redis for scalability, or files for small-scale apps) to track requests across users/requests.
Option 1: Using Redis (Recommended for High Traffic)
Redis is ideal because its atomic operations avoid race conditions, which is critical for accurate rate limiting.
<?php // Connect to Redis (adjust host/port as needed) $redis = new Redis(); $redis->connect('localhost', 6379); // Generate a key for the current minute (e.g., "api_requests_202405201435") $currentMinute = date('YmdHi'); $requestKey = "api_requests_{$currentMinute}"; // Increment the request count atomically $requestCount = $redis->incr($requestKey); // Set the key to expire after 1 minute (so we don't store old data) if ($requestCount === 1) { $redis->expire($requestKey, 60); } // Apply delay if we exceed the 20 requests/minute limit if ($requestCount > 20) { // Calculate delay: e.g., 1 second extra for each request over the limit $delay = ($requestCount - 20) * 1; echo "Rate limit hit! Delaying request by {$delay} seconds...\n"; sleep($delay); // Use usleep() for millisecond precision if needed } // Proceed with your API call here echo "Processing request {$requestCount}...\n"; ?>
Option 2: Using File Storage (For Small-Scale Apps)
If you don't have Redis, a file-based approach works for low-traffic systems—just be aware of potential race conditions (you can add file locking to mitigate this).
<?php $storageFile = __DIR__ . '/request_tracker.json'; // Initialize storage if file doesn't exist if (!file_exists($storageFile)) { file_put_contents($storageFile, json_encode([])); } // Lock the file to prevent race conditions $fileHandle = fopen($storageFile, 'r+'); flock($fileHandle, LOCK_EX); // Read and parse request data $requestData = json_decode(fread($fileHandle, filesize($storageFile)), true); // Clean up requests older than 1 minute $currentTime = time(); $requestData = array_filter($requestData, fn($timestamp) => $currentTime - $timestamp < 60); // Add current request time $requestData[] = $currentTime; $requestCount = count($requestData); // Write back updated data ftruncate($fileHandle, 0); fwrite($fileHandle, json_encode($requestData)); flock($fileHandle, LOCK_UN); fclose($fileHandle); // Apply delay if over limit if ($requestCount > 20) { $delay = ($requestCount - 20) * 1; echo "Rate limit exceeded! Delaying by {$delay} seconds.\n"; sleep($delay); } // Continue with API logic echo "Handling request #{$requestCount}\n"; ?>
Symfony Implementation
Symfony has a built-in RateLimiter component that simplifies this process—no need to reinvent the wheel.
Step 1: Install the Component
composer require symfony/rate-limiter
Step 2: Configure the Rate Limiter
Add this to config/packages/rate_limiter.yaml:
framework: rate_limiter: api_requests: policy: fixed_window limit: 20 interval: 1 minute # For distributed systems, use a Redis storage adapter instead of in-memory # storage: Symfony\Component\RateLimiter\Storage\RedisStorage
Step 3: Use the Limiter in Your Controller/Service
Inject the rate limiter factory into your controller, then apply delays when the limit is hit:
<?php namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\RateLimiter\RateLimiterFactoryInterface; class ApiController extends AbstractController { public function index(RateLimiterFactoryInterface $apiRequestsLimiter): Response { $limiter = $apiRequestsLimiter->create('api_requests'); $limit = $limiter->consume(); // If we've exceeded the limit, calculate and apply delay if (!$limit->isAccepted()) { $retryAfter = $limit->getRetryAfter(); $delay = $retryAfter->getTimestamp() - time(); echo "Rate limit reached! Waiting {$delay} seconds...\n"; sleep($delay); // After waiting, consume again (or proceed directly) $limit = $limiter->consume(); } // Your API logic here return new Response("Request processed successfully!"); } }
Key Notes
- Distributed Systems: For multi-server setups, use Redis (or another shared storage) instead of in-memory storage to ensure consistent rate tracking across instances.
- Delay Strategy: Adjust the delay calculation (e.g., linear vs. exponential) based on your needs—exponential delays can help prevent overwhelming the API further.
- Atomic Operations: Always use atomic operations (like Redis
incr) or file locking to avoid counting requests incorrectly during high concurrency.
内容的提问来源于stack exchange,提问作者Cagrison

