简体   繁体   中英

What's the best approach to rate limit an expensive operation with PHP & Memcached?

I came up with this:

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);

Any thought on this, would it be beneficial to sleep for a few seconds if the IP is found in Memcached?

Seems like a pretty good solution, though perhaps you could use the the session_id() instead of the ip address. This way if you're dealing with people behind a router, you won't block persons who aren't hammering. Though the session_id could easily be regenerated by them clearing their cookies, but it'll probably take them longer to do that, than to just wait the 5 seconds. You definitely do not want to sleep in a PHP script as that just holds up a PHP process while sleeping.

You could set up another memcache item to track how many times they've hit the warning, within say a 1hr period and then you could do something more harsh, or log the user information down.

Though might be best to try to optimize the operation so it's not as costly(easier said than done).

You can use the token bucket algorithm for rate limiting. I've implemented that for you: bandwidth-throttle/token-bucket

I would also recommend not to sleep, as you do block resources of your server. Just exit with the HTTP status code 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();

But if you really want to sleep you can do this with a BlockingConsumer :

$consumer = new BlockingConsumer($bucket);
$consumer->consume(1);
foo();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM