Каков наилучший подход для ограничения скорости дорогостоящей операции с PHP и Memcached? - PullRequest
2 голосов
/ 25 марта 2010

Я придумал это:

if($prog->memcache) {
    $r = $prog->memcache->get("ratelimit:{$_SERVER['REMOTE_ADDR']}");
    if(!empty($r)) $prog->errorClose('This IP has been flagged for potential abuse.');
}

foo(); // the thing we're rate limiting...

if($prog->memcache)
    $prog->memcache->set("ratelimit:{$_SERVER['REMOTE_ADDR']}", 1, 0, 5);

Есть мысли об этом, было бы полезно поспать несколько секунд, если IP-адрес найден в Memcached?

Ответы [ 2 ]

1 голос
/ 25 марта 2010

Похоже, довольно хорошее решение, хотя, возможно, вы могли бы использовать session_id () вместо IP-адреса.Таким образом, если вы имеете дело с людьми за маршрутизатором, вы не будете блокировать людей, которые не бьют.Хотя session_id может быть легко сгенерирован, когда они очистят свои куки, но, вероятно, им потребуется больше времени, чем просто подождать 5 секунд.Вы определенно не хотите спать в скрипте PHP, поскольку он просто поддерживает процесс PHP во время сна.

Вы можете настроить другой элемент memcache, чтобы отслеживать, сколько раз они нажимали предупреждение, например,1 час, а затем вы можете сделать что-то более резкое или записать информацию о пользователе.

Хотя может быть лучше попытаться оптимизировать операцию, чтобы она была не такой дорогой (легче сказать, чем сделать).

0 голосов
/ 12 июля 2015

Вы можете использовать алгоритм корзины токенов для ограничения скорости. Я реализовал это для вас: 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();
...