Ограничитель частоты
Webman Ограничитель частоты, поддерживающий аннотированное ограничение частоты.
Поддерживает драйверы apcu, redis, memory.
Адрес исходного кода
https://github.com/webman-php/rate-limiter
Установка
composer require webman/rate-limiter
Использование
<?php
namespace app\controller;
use RuntimeException;
use Webman\RateLimiter\Annotation\RateLimiter;
class UserController
{
#[RateLimiter(limit: 10)]
public function index(): string
{
// По умолчанию ограничение частоты по IP, единица времени по умолчанию 1 секунда
return 'Не более 10 запросов с каждого IP в секунду';
}
#[RateLimiter(limit: 100, ttl: 60, key: RateLimiter::UID)]
public function search(): string
{
// key: RateLimiter::UID, ограничение по частоте на основе ID пользователя, требует, чтобы session('user.id') не была пустой
return 'Не более 100 поисков для каждого пользователя за 60 секунд';
}
#[RateLimiter(limit: 1, ttl: 60, key: RateLimiter::SID, message: 'Каждый человек может отправить только 1 письмо в минуту')]
public function sendMail(): string
{
// key: RateLimiter::SID, ограничение по частоте на основе session_id
return 'Письмо успешно отправлено';
}
#[RateLimiter(limit: 100, ttl: 24*60*60, key: 'coupon', message: 'Сегодня все купоны уже выданы, приходите завтра')]
#[RateLimiter(limit: 1, ttl: 24*60*60, key: RateLimiter::UID, message: 'Каждый пользователь может получить купон только один раз в день')]
public function coupon(): string
{
// key: 'coupon', здесь coupon - это пользовательский ключ, глобально ограничение частоты по ключу coupon, не более 100 купонов в день
// Одновременно ограничение по пользователям, каждый пользователь может получить купон только один раз в день
return 'Купон успешно выдан';
}
#[RateLimiter(limit: 5, ttl: 24*60*60, key: [UserController::class, 'getMobile'], message: 'Не более 5 SMS с каждого номера телефона в день')]
public function sendSms2(): string
{
// Когда ключ является переменной, можно использовать способ [класс, статический метод] для получения ключа, например [UserController::class, 'getMobile'] вызовет метод getMobile() класса UserController для получения ключа
return 'SMS успешно отправлено';
}
/**
* Пользовательский ключ, получение номера телефона, должен быть статическим методом
* @return string
*/
public static function getMobile(): string
{
return request()->get('mobile');
}
#[RateLimiter(limit: 1, ttl: 10, key: RateLimiter::IP, message: 'Частота ограничена', exception: RuntimeException::class)]
public function testException(): string
{
// При превышении лимита, по умолчанию возбуждается исключение Webman\RateLimiter\RateLimitException, можно изменить через параметр exception
return 'ok';
}
}
Пояснение
- По умолчанию единица времени составляет 1 секунду
- Можно установить единицу времени с помощью ttl, например
ttl:60
это 60 секунд - По умолчанию ограничение частоты по IP (по умолчанию
127.0.0.1
не ограничивается, см. раздел конфигурации ниже) - Встроенное ограничение по IP, UID (требует, чтобы
session('user.id')
не была пустой) и SID (ограничение поsession_id
) - Если используется прокси nginx, для ограничения по IP необходимо передавать заголовок
X-Forwarded-For
, см. nginx прокси - При превышении лимита будет возбуждено исключение
Webman\RateLimiter\RateLimitException
, можно использоватьexception:xx
для определения пользовательского класса исключения - При возбуждении исключения, сообщение об ошибке по умолчанию
Too Many Requests
, можно задать пользовательское сообщение черезmessage:xx
- Сообщение об ошибке по умолчанию также можно изменить с помощью многоязычности, в Linux воспользуйтесь следующими командами
composer require symfony/translation mkdir resource/translations/zh_CN/ -p echo "<?php return [ 'Too Many Requests' => 'Частота запросов ограничена' ];" > resource/translations/zh_CN/messages.php php start.php restart
Интерфейс
Иногда разработчики хотят напрямую вызывать ограничитель частоты в коде, смотрите следующий код
<?php
namespace app\controller;
use RuntimeException;
use Webman\RateLimiter\Limiter;
class UserController {
public function sendSms(string $mobile): string
{
// Здесь mobile выступает как ключ
Limiter::check($mobile, 5, 24*60*60, 'Не более 5 SMS с каждого номера телефона в день');
return 'SMS успешно отправлено';
}
}
Настройка
config/plugin/webman/rate-limiter/app.php
<?php
return [
'enable' => true,
'driver' => 'auto', // auto, apcu, memory, redis
'stores' => [
'redis' => [
'connection' => 'default',
]
],
// Запросы с этих IP не подлежат ограничениям по частоте (эффективно только при ключе RateLimiter::IP)
'ip_whitelist' => [
'127.0.0.1',
],
];
- enable: Включить ли ограничение частоты
- driver: Одно из значений
auto
,apcu
,memory
,redis
, при использованииauto
будет автоматически выбран один изapcu
илиmemory
- stores: Конфигурация
redis
,connection
соответствует ключу вconfig/redis.php
- ip_whitelist: IP-адреса из белого списка не будут подлежать ограничению частоты (эффективно только при ключе
RateLimiter::IP
)
Выбор драйвера
memory
-
Описание
Не требует установки дополнительных расширений, лучшая производительность. -
Ограничения использования
Ограничение частоты действует только в рамках текущего процесса, данные по ограничению не разделяются между несколькими процессами, также не поддерживает кластерное ограничение. -
Подходящие сценарии
Среда разработки Windows; бизнес, который не требует строгого ограничения частоты; защита от CC-атак.
apcu
-
Установка расширения
Необходимо установить расширение apcu и настроить в php.iniapc.enabled=1 apc.enable_cli=1
Если не знаете, где находится php.ini, можно найти его с помощью команды
php --ini
-
Описание
Производительность немного ниже, чем у memory; поддерживает совместное использование данных по ограничению частоты между несколькими процессами. -
Ограничения использования
Не поддерживает кластерные системы -
Подходящие сценарии
Любая среда разработки; ограничение частоты на одной машине в продакшене; сценарии, где кластеру не нужно строгое ограничение частоты; защита от CC-атак.
redis
-
Зависимости
Необходимо установить расширение redis и компонент Redis, команда установкиcomposer require -W illuminate/redis illuminate/events
-
Описание
Производительность ниже, чем у apcu; поддерживает точное ограничение частоты как на одной машине, так и в кластере. -
Подходящие сценарии
Среда разработки; однопроцессная среда; кластерная среда