Каков наилучший подход для ограничения скорости дорогостоящей операции с PHP и Memcached?
Я придумал это:
if ($ prog-> memcache) { $ r = $ prog-> memcache-> get ("ratelimit: {$ _ SERVER ['REMOTE_ADDR']}"); if (! empty ($ r)) $ prog-> errorClose ('Этот IP-адрес помечен для возможного злоупотребления.'); } Foo(); // вещь, которую мы ограничиваем если ($ PROG-> кэшу) $prog->memcache->set("ratelimit:{$_SERVER['REMOTE_ADDR']}", 1, 0, 5);
Любая мысль об этом, было бы полезно поспать несколько секунд, если IP обнаружен в Memcached?
2 ответа
Похоже, довольно хорошее решение, хотя, возможно, вы могли бы использовать session_id() вместо IP-адреса. Таким образом, если вы имеете дело с людьми за маршрутизатором, вы не будете блокировать людей, которые не бьют. Хотя session_id может быть легко сгенерирован, когда они очистят свои куки, но, вероятно, им потребуется больше времени, чем просто подождать 5 секунд. Вы определенно не хотите спать в скрипте PHP, поскольку он просто поддерживает процесс PHP во время сна.
Вы можете настроить другой элемент memcache, чтобы отслеживать, сколько раз они нажали предупреждение, например, за 1 час, а затем вы могли бы сделать что-то более резкое или записать информацию пользователя.
Хотя может быть лучше попытаться оптимизировать работу, чтобы она не была такой дорогой (легче сказать, чем сделать).
Вы можете использовать алгоритм токена для ограничения скорости. Я реализовал это для вас: https://github.com/bandwidth-throttle/token-bucket
Я также рекомендовал бы не спать, так как вы блокируете ресурсы своего сервера. Просто выйдите с кодом состояния HTTP 429:
use bandwidthThrottle\tokenBucket\Rate;
use bandwidthThrottle\tokenBucket\TokenBucket;
use bandwidthThrottle\tokenBucket\storage\MemcachedStorage;
$storage = new MemcachedStorage("resource", $memcached);
$rate = new Rate(10, Rate::SECOND);
$bucket = new TokenBucket(10, $rate, $storage);
$bucket->bootstrap(10);
if (!$bucket->consume(1, $seconds)) {
http_response_code(429);
header(sprintf("Retry-After: %d", floor($seconds)));
exit();
}
foo();
Но если вы действительно хотите спать, вы можете сделать это с BlockingConsumer
:
$consumer = new BlockingConsumer($bucket);
$consumer->consume(1);
foo();