Limitador de tasa
Limitador de tasa de webman con soporte de limitación por anotación.
Soporta drivers apcu, redis y memory.
Repositorio fuente
https://github.com/webman-php/limiter
Instalación
composer require webman/limiter
Uso
<?php
namespace app\controller;
use RuntimeException;
use support\limiter\annotation\Limit;
class UserController
{
#[Limit(limit: 10)]
public function index(): string
{
// Por defecto limitación por IP, ventana de tiempo por defecto 1 segundo
return 'Máximo 10 peticiones por IP por segundo';
}
#[Limit(limit: 100, ttl: 60, key: Limit::UID)]
public function search(): string
{
// key: Limit::UID, limitación por ID de usuario, requiere session('user.id') no vacía
return 'Máximo 100 búsquedas por usuario por 60 segundos';
}
#[Limit(limit: 1, ttl: 60, key: Limit::SID, message: 'Solo 1 email por persona por minuto')]
public function sendMail(): string
{
// key: Limit::SID, limitación por session_id
return 'Email enviado correctamente';
}
#[Limit(limit: 100, ttl: 24*60*60, key: 'coupon', message: 'Cupones de hoy agotados, intente mañana')]
#[Limit(limit: 1, ttl: 24*60*60, key: Limit::UID, message: 'Cada usuario solo puede reclamar un cupón por día')]
public function coupon(): string
{
// key: 'coupon', clave personalizada para limitación global, máx 100 cupones por día
// También limitación por ID de usuario, cada usuario un cupón por día
return 'Cupón enviado correctamente';
}
#[Limit(limit: 5, ttl: 24*60*60, key: [UserController::class, 'getMobile'], message: 'Máximo 5 SMS por número por día')]
public function sendSms2(): string
{
// Cuando key es variable: [clase, método_estático], ej. [UserController::class, 'getMobile'] usa el valor de retorno de UserController::getMobile() como clave
return 'SMS enviado correctamente';
}
/**
* Clave personalizada, obtener número móvil, debe ser método estático
* @return string
*/
public static function getMobile(): string
{
return request()->get('mobile');
}
#[Limit(limit: 1, ttl: 10, key: Limit::IP, message: 'Tasa limitada', exception: RuntimeException::class)]
public function testException(): string
{
// Excepción por defecto al exceder: support\limiter\RateLimitException, modificable vía parámetro exception
return 'ok';
}
}
Notas
- Usa algoritmo de ventana fija
- Ventana de tiempo ttl por defecto: 1 segundo
- Configurar ventana vía ttl, ej.
ttl:60para 60 segundos - Dimensión de limitación por defecto: IP (por defecto
127.0.0.1no limitado, ver configuración abajo) - Integrado: limitación IP, UID (requiere
session('user.id')no vacía), SID (porsession_id) - Con proxy nginx, pasar cabecera
X-Forwarded-Forpara limitación IP, ver proxy nginx - Dispara
support\limiter\RateLimitExceptional exceder, clase de excepción personalizada víaexception:xx - Mensaje de error por defecto al exceder:
Too Many Requests, mensaje personalizado víamessage:xx - Mensaje de error por defecto modificable vía traducción, referencia 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
API
A veces los desarrolladores quieren llamar al limitador directamente en el código, ver el siguiente ejemplo:
<?php
namespace app\controller;
use RuntimeException;
use support\limiter\Limiter;
class UserController {
public function sendSms(string $mobile): string
{
// mobile se usa como clave aquí
Limiter::check($mobile, 5, 24*60*60, 'Máximo 5 SMS por número por día');
return 'SMS enviado correctamente';
}
}
Configuración
config/plugin/webman/limiter/app.php
<?php
use support\limiter\RateLimitException;
return [
'enable' => true,
'driver' => 'auto', // auto, apcu, memory, redis
'stores' => [
'redis' => [
'connection' => 'default',
]
],
// Estas IP no están limitadas (solo efectivo cuando key es Limit::IP)
'ip_whitelist' => [
'127.0.0.1',
],
'exception' => RateLimitException::class
];
- enable: Activar limitación de tasa
- driver: Uno de
auto,apcu,memory,redis;autoelige automáticamente entreapcu(prioritario) ymemory - stores: Configuración Redis,
connectioncorresponde a la clave enconfig/redis.php - ip_whitelist: Las IP en lista blanca no están limitadas (solo efectivo cuando key es
Limit::IP)
Selección de driver
memory
-
Introducción
Sin extensiones requeridas, mejor rendimiento. -
Limitaciones
Limitación solo para el proceso actual, sin compartir datos entre procesos, limitación en cluster no soportada. -
Casos de uso
Entorno de desarrollo Windows; escenarios sin limitación estricta; defensa contra ataques CC.
apcu
- Instalación de extensión
Extensión apcu requerida, configuración php.ini:
apc.enabled=1
apc.enable_cli=1
Ubicación de php.ini con php --ini
-
Introducción
Excelente rendimiento, soporta compartir datos multi-proceso. -
Limitaciones
Cluster no soportado -
Casos de uso
Cualquier entorno de desarrollo; limitación servidor único en producción; cluster sin limitación estricta; defensa contra ataques CC.
redis
- Dependencias
Extensión redis y componente Redis requeridos, instalación:
composer require -W webman/redis illuminate/events
-
Introducción
Rendimiento inferior a apcu, soporta limitación precisa servidor único y cluster -
Casos de uso
Entorno de desarrollo; servidor único en producción; entorno cluster