Giter Site home page Giter Site logo

token-bucket's Introduction

Bandwidth Throttle

This library implements traffic shaping on streams (input and output streams).

Installation

Use Composer:

composer require bandwidth-throttle/bandwidth-throttle

Usage

The package is in the namespace bandwidthThrottle.

BandwidthThrottle::setRate() sets the bandwidth limit. E.g. this would set it to 100KiB/s:

$throttle->setRate(100, bandwidthThrottle\BandwidthThrottle::KIBIBYTES);

BandwidthThrottle::throttle() throttles a stream. After that any stream operation (e.g. fread()) will be limited to the throttle rate.

Optional methods

BandwidthThrottle::setBurstCapacity() sets the burst capacity. This is the capacity which can be accumulated while the stream is not in use. Accumulated capacity can be consumed instantly. Per default this the amount of bytes for one second from the rate.

BandwidthThrottle::setInitialBurst() sets the initial burst. Per default the throttle starts with 0 accumulated bytes. Setting an initial burst makes that amount of bytes instantly available.

BandwidthThrottle::setStorage() sets the storage for the underlying token bucket. The storage determines the scope of the bucket. The default storage is in the request scope. I.e. it will limit the rate per request. There are storages which can be shared amongst requests.

BandwidthThrottle::setThrottleBothStreams() will apply the throttle for both input and output streams. This is the default mode.

BandwidthThrottle::setThrottleInputStream() will apply the throttle for the input stream only.

BandwidthThrottle::setThrottleOutputStream() will apply the throttle for the output stream only.

BandwidthThrottle::unthrottle() removes the throttle from the stream.

Example

This example will stream a video with a rate of 100KiB/s to the browser:

use bandwidthThrottle\BandwidthThrottle;

$in  = fopen(__DIR__ . "/video.mpg", "r");
$out = fopen("php://output", "w");

$throttle = new BandwidthThrottle();
$throttle->setRate(100, BandwidthThrottle::KIBIBYTES); // Set limit to 100KiB/s
$throttle->throttle($out);

stream_copy_to_stream($in, $out);

A more sophisticated scenario would be applying multiple throttles on one resource. E.g. the overall bandwidth for the host should be throttled to 1MiB/s and 100KiB/s per request. This will require a shared storage for the 1MiB/s:

use bandwidthThrottle\BandwidthThrottle;
use bandwidthThrottle\tokenBucket\storage\FileStorage;

$in  = fopen(__DIR__ . "/video.mpg", "r");
$out = fopen("php://output", "w");

$hostThrottle = new BandwidthThrottle();
$hostThrottle->setRate(1, BandwidthThrottle::MEBIBYTES); // Set limit to 1MiB/s
$hostThrottle->setStorage(new FileStorage(__DIR__ . "/host.throttle"));
$hostThrottle->throttle($out);

$requestThrottle = new BandwidthThrottle();
$requestThrottle->setRate(100, BandwidthThrottle::KIBIBYTES); // Set limit to 100KiB/s
$requestThrottle->throttle($out);

stream_copy_to_stream($in, $out);

License and authors

This project is free and under the WTFPL. Responsible for this project is Markus Malkusch [email protected].

Donations

If you like this project and feel generous donate a few Bitcoins here: 1335STSwu9hST4vcMRppEPgENMHD2r1REK

Build Status

token-bucket's People

Contributors

austinpray avatar deefour avatar drsect0r avatar lloy0076 avatar malkusch avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

token-bucket's Issues

"Could not write to storage" for FileStorage

When I use next code for bootstrap token bucket:

use bandwidthThrottle\tokenBucket\Rate;
use bandwidthThrottle\tokenBucket\TokenBucket;
use bandwidthThrottle\tokenBucket\storage\FileStorage;

$storage = new FileStorage(__DIR__ . "/api.bucket");
$rate = new Rate(10, Rate::SECOND);
$bucket = new TokenBucket(10, $rate, $storage);
$bucket->bootstrap(10);

On Linux and macOS all works well, but in Windows I get an error:

Could not write to storage.

Maybe it's because the file opens twice, first, in FileStorage::__construct, then file locked by flock and then file opens in FileStorage::bootstrap method.

Rate limiting API requests within multiple workers

My use case is :

  • A set of 5 (rabbitmq) workers consume messages, doing one ore more API request per message
  • No more than 10 API requests should be globally (throughout all workers) done for 1 second

Would this repo implementation be fine with this distributed constraint ?
Would this rate of 10 be an absolute limit or an average ?

Probable memory eating for Memcached storage

I am talking about Memcached storage implementation (probably other too).
Since you use memcached::set with ttl = 0, old unused buckets will stay in memory forever, and this is not good for websites with bucket-per-ip. For websites with 100000 visitors per day it will eat >100 bytes per record = 10Megabytes, and will grow each day. In a month it will eat ~ 300Megabytes and some vps servers will go down.
I think it should be possible to set ttl to specific reasonable value.

BlockingConsumer: timeout parameter?

Hey. I'm not sure whether a complete deadlock during blocking consumption could somehow occur or not, but isn't it generally healthy to have a failsafe for a blocking operation - such as a configurable timeout setting?

Feature request: refund tokens

Thanks for such a nice pkg! I wonder if there is any method like refund or something that can return the tokens to the bucket.

I used this feature mainly for trade amount limitation. In such a case:

// ...
// consume when an order creating.
$bucket->consume($order->total);
$order->create();
// then order turns to failed.
$order->failed();
$bucket->refund($order->total);

For now I implement this feature on my own. But I think that would be better if there is official support :)

Is is possible to use SessionScope to throttle requests by IP address?

Is it possible to use SessionScope to limit requests by an unique identifier such as their IP address?

Ideally something that would be able to 'kick' requests if an IP has made over a set allocation within a set time frame, e.g if the limit is 1000 requests per hour, once they make the 1001th request within that window rather than sleep it will just discard the request until they allowed to make requests again in the next window.

Timeout of 3 seconds exceeded

I get this error report everytime after some problem happened.Then I review the code in "vendor\malkusch\lock"and find some bug for Redis:If u set a lock for the bucket and the client broke,this lock will never be release so that every request will be stoped by the "setnx".I wonder if u have some solution for this bug,and sorry for my English :p
by zhangxiaohou

Leaky Bucket vs Token Bucket

This project is great, but relies upon bootstrapping the bucket (ie filling with initial token allocation)

This can be troublesome when dealing with multiple resources that need limiting - eg rate limiting an API by remote IP address. There's no simple way of knowing if you've already bootstrapped for that particular IP without calling the storage, which is an extra overhead.

Instead, I'd propose a leaky bucket design - instead of checking the bucket still has >0 tokens remaining, you reverse it. So the bucket is empty initially, then you 'drain' X tokens per X seconds and each new rate limited request adds a token.

If the bucket is 'full' (ie tokens > limit, means you are filling faster than emptying, but with a 'limit' sized buffer) then you fail the request.

This way, you never need to bootstrap - you always just increment the bucket counter.

Thoughts?

MemcachedStorage CASMutex TimeoutException

The current implementation of MemcachedStorage doesn't work.

When the consume method is called and "$delta < 0", then $this->storage->getMutex()->synchronized keeps looping the "callable $code". This is because "$this->storage->getMutex()->notify()" is never called when "$delta < 0", so "$this->loop->execute($code)" (CASMutex) keeps running and will reach the timeout "malkusch\lock\exception\TimeoutException" "Timeout of 3 seconds exceeded.".

The current implementation of MemcachedStorage uses CAS, where the MemcacheStorage (without D) implementation doesn't. I've created a MemcachedStorage implementation without CAS (also using "MemcachedMutex" instead of "CASMutex") and that seems to work fine. What was the reason to use the CAS tokens?

Tokens left

Hi!

Is there any way to know/access to how many tokens are left until reach the time limit?

Thanks!

Ideal?

I want to rate limit 500 requests an hour to a specific script, by their IP Address.

Is this a good script to do so, any examples?

ext-redis

Is there a way you can make that dependency optional?

Using as client

Hello I am just curious if I could use the lib to make calls to an API rest without surpass their limits.

Does it support redis cluster?

An error happens when I use redis cluster,
Uncaught TypeError: Argument 2 passed to bandwidthThrottle\tokenBucket\storage\PHPRedisStorage::__construct() must be an instance of Redis, instance of RedisCluster given

Fatal Error: The string is not 64 bit long. PHP 8.2

Fatal error: Uncaught bandwidthThrottle\tokenBucket\storage\StorageException:
The string is not 64 bit long. in \bandwidth-throttle\token-bucket\classes\util\DoublePacker.php: 41

Having this error when applied following code in constructor of ApiController, and tried to use consume:

`function __construct() {

    $this->storage = new FileStorage(__DIR__ . "/api.bucket");
    $this->rate    = new Rate(10, Rate::SECOND);
    $this->bucket  = new TokenBucket(10, $this->rate, $this->storage);
     //Following code executed once, then commented
     //$this->bucket->bootstrap(10);

}`

`protected function checkRateLimit() {

    if (!$this->bucket->consume(1, $seconds)) {
        http_response_code(429);
        header(sprintf("Retry-After: %d", floor($seconds)));
        exit();
    }
    //echo "Continue to API response";
    return true;

}`

I have also noticed FileStorage class is sending blank string to unpack( ) function in Double Packer on line 129.

bandwidthThrottle\tokenBucket\util\DoublePacker: :unpack('')

Please guide what's going wrong here?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.