Rate Limiting в PHP: защита API и оптимизация нагрузки
Rate limiting (ограничение скорости запросов) — критически важный механизм для любого веб-приложения на PHP, особенно для публичных API. Он предотвращает злоупотребления, защищает от DDoS-атак, снижает нагрузку на сервер и обеспечивает справедливое распределение ресурсов между пользователями. В этой статье мы подробно разберем популярные библиотеки и инструменты для реализации rate limiting в PHP.
Основные алгоритмы rate limiting
Прежде чем выбирать библиотеку, важно понимать базовые алгоритмы:
- Token Bucket (Токеновое ведро) — самый гибкий алгоритм. Ведро содержит N токенов, каждый запрос забирает токен. Токены восстанавливаются с заданной скоростью.
- Leaky Bucket (Дырявое ведро) — запросы обрабатываются с постоянной скоростью. Избыточные запросы отбрасываются или ставятся в очередь.
- Fixed Window (Фиксированное окно) — запросы считаются за фиксированный интервал (например, 60 секунд). Простой, но может пропускать пиковые нагрузки на границах окон.
- Sliding Window (Скользящее окно) — более точный алгоритм, учитывающий запросы в плавающем временном окне. Использует логи или счетчики с временными метками.
Популярные библиотеки для rate limiting в PHP
1. Symfony Rate Limiter (symfony/rate-limiter)
Официальный компонент Symfony, но работает и как standalone-библиотека. Поддерживает все основные алгоритмы: Token Bucket, Fixed Window, Sliding Window. Идеально подходит для проектов на Symfony или Laravel.
use Symfony\Component\RateLimiter\RateLimiterFactory;
use Symfony\Component\RateLimiter\Storage\InMemoryStorage;
$factory = new RateLimiterFactory([
'id' => 'api',
'policy' => 'token_bucket',
'limit' => 100,
'rate' => ['interval' => '1 minute'],
], new InMemoryStorage());
$limiter = $factory->create('user_123');
if ($limiter->consume(1)->isAccepted()) {
// Обработка запроса
} else {
// Возврат 429 Too Many Requests
header('HTTP/1.1 429 Too Many Requests');
echo json_encode(['error' => 'Rate limit exceeded']);
}
2. nikic/FastRoute с middleware rate limiting
Легковесное решение для проектов без фреймворков. Использует PHP-реализацию Token Bucket с хранением в Redis или Memcached.
class RateLimitMiddleware {
private $redis;
private $maxRequests = 100;
private $window = 60; // секунд
public function handle($request, $next) {
$key = 'rate_limit:' . $_SERVER['REMOTE_ADDR'];
$current = $this->redis->incr($key);
if ($current === 1) {
$this->redis->expire($key, $this->window);
}
if ($current > $this->maxRequests) {
return new Response('Too Many Requests', 429);
}
return $next($request);
}
}
3. Laravel Rate Limiter (встроенный)
В Laravel rate limiting встроен в ядро. Использует Redis или кэш. Поддерживает именованные лимитеры и сегментирование по пользователям.
// В RouteServiceProvider
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
protected function configureRateLimiting()
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
}
// В контроллере
public function index()
{
$executed = RateLimiter::attempt(
'send-message:'.$user->id,
$perMinute = 5,
function() {
// Отправка сообщения
}
);
if (! $executed) {
return response('Too Many Attempts.', 429);
}
}
4. PHP RateLimit (php-rateLimit/php-rateLimit)
Универсальная библиотека, поддерживающая множество бэкендов: файлы, Redis, PDO, APCu. Реализует алгоритмы Sliding Window и Token Bucket.
use RateLimit\RateLimiter;
use RateLimit\Adapter\RedisAdapter;
use RateLimit\Adapter\FileAdapter;
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$adapter = new RedisAdapter($redis);
$rateLimiter = new RateLimiter($adapter, 100, 3600); // 100 запросов в час
if ($rateLimiter->limit('user:'.$userId)) {
// Запрос разрешен
} else {
// Превышен лимит
}
Практические рекомендации
- Выбирайте алгоритм