The token bucket algorithm can be used for controlling the usage rate of a resource. The scope of that rate is determined by the underlying storage. Example: 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); if (!$bucket->consume(1, $seconds)) { http_response_code(429); header(sprintf("Retry-After: %d", floor($seconds))); exit(); }
Author: Markus Malkusch (markus@malkusch.de)
Ejemplo n.º 1
0
 /**
  * Initialize the a new token bucket, then proload that bucket with tokens
  * @param  string       $bucketName The name of the bucket to create
  * @param  Rate         $fillRate   Rate Object used to define fill rate
  * @return TokenBucket              The initialized bucket
  * @access private
  */
 private function initializeBucket($bucketName, Rate $fillRate)
 {
     $storage = new PHPRedisStorage($bucketName, $this->redisObj);
     $bucket = new TokenBucket(self::MAXBUCKETSIZE, $fillRate, $storage);
     $bucket->bootstrap(self::MAXBUCKETSIZE);
     return $bucket;
 }
Ejemplo n.º 2
0
 /**
  * Test the capacity limit of the bucket
  *
  * @test
  */
 public function testCapacity()
 {
     $rate = new Rate(1, Rate::SECOND);
     $tokenBucket = new TokenBucket(10, $rate, new SingleProcessStorage());
     $tokenBucket->bootstrap();
     sleep(11);
     $this->assertTrue($tokenBucket->consume(10));
     $this->assertFalse($tokenBucket->consume(1));
 }
 /**
  * Tests traffic shapping filtering.
  *
  * @param float  $expectedDuration The expected duration in seconds.
  * @param int[]  $bytes            The amount of bytes to write.
  *
  * @test
  * @dataProvider provideTestFilterShapesTraffic
  */
 public function testFilterShapesTraffic($expectedDuration, array $bytes)
 {
     $stream = fopen("php://memory", "w");
     $bucket = new TokenBucket(10, new Rate(1, Rate::SECOND), new SingleProcessStorage());
     $bucket->bootstrap();
     stream_filter_register("test", TokenBucketFilter::class);
     $this->filter = stream_filter_append($stream, "test", STREAM_FILTER_WRITE, $bucket);
     $time = microtime(true);
     foreach ($bytes as $byte) {
         fwrite($stream, str_repeat(" ", $byte));
     }
     fclose($stream);
     $this->assertLessThan(0.001, abs(microtime(true) - $time - $expectedDuration));
 }
 /**
  * Throttles a stream to the given rate.
  *
  * This registers a filter to the given stream which does the traffic
  * shaping. After that any stream operation is throttled.
  *
  * The stream can be an input or an output stream.
  *
  * This object can throttle only one stream at a time. If you want to
  * call throttle() again, make either sure you called {@link unthrottle()}
  * before or use a new instance.
  *
  * @param resource $stream The stream.
  *
  * @throws BandwidthThrottleException Error during throtteling the stream.
  * @throws \LengthException The initial burst size was greater than the burst size.
  */
 public function throttle($stream)
 {
     try {
         if (is_resource($this->filter)) {
             throw new BandwidthThrottleException("This throttle is still attached to a stream. Call unthrottle() or use a new instance.");
         }
         $this->registerOnce();
         $capacity = empty($this->capacity) ? $this->rate->getTokensPerSecond() : $this->capacity;
         $bucket = new TokenBucket($capacity, $this->rate, $this->storage);
         $bucket->bootstrap($this->initialTokens);
         $this->filter = stream_filter_append($stream, self::FILTER_NAME, $this->filterMode, $bucket);
         if (!is_resource($this->filter)) {
             throw new BandwidthThrottleException("Could not throttle the stream.");
         }
     } catch (StorageException $e) {
         throw new BandwidthThrottleException("Could not initialize token bucket.", 0, $e);
     }
 }
 /**
  * Tests consume() won't sleep less than one millisecond.
  *
  * @test
  */
 public function testMinimumSleep()
 {
     $rate = new Rate(10, Rate::MILLISECOND);
     $bucket = new TokenBucket(1, $rate, new SingleProcessStorage());
     $bucket->bootstrap();
     $consumer = new BlockingConsumer($bucket);
     $time = microtime(true);
     $consumer->consume(1);
     $this->assertLessThan(1.0E-5, abs(microtime(true) - $time - 0.001));
 }
 /**
  * After waiting longer than the complete refill period on an empty bucket,
  * getTokens() should return the capacity of the bucket.
  *
  * @test
  */
 public function getTokensShouldReturnCapacityAfterWaitingLongerThanRefillPeriod()
 {
     $rate = new Rate(1, Rate::SECOND);
     $bucket = new TokenBucket(10, $rate, new SingleProcessStorage());
     $bucket->bootstrap(0);
     sleep(11);
     $this->assertEquals(10, $bucket->getTokens());
 }