php
46 lines · 7 steps
How a token bucket rate limiter works
A token bucket smooths request bursts by refilling credits over time and blocking callers until enough tokens are available.
Explained by
highlit
1final class TokenBucketLimiter
2{
3 private float $tokens;
4 private float $lastRefill;
5
6 public function __construct(
7 private readonly float $capacity,
8 private readonly float $refillPerSecond
9 ) {
10 $this->tokens = $capacity;
11 $this->lastRefill = microtime(true);
12 }
13
14 public function acquire(float $cost = 1.0): void
15 {
16 if ($cost > $this->capacity) {
17 throw new InvalidArgumentException('Cost exceeds bucket capacity');
18 }
19
20 while (true) {
21 $this->refill();
22
23 if ($this->tokens >= $cost) {
24 $this->tokens -= $cost;
25 return;
26 }
27
28 $deficit = $cost - $this->tokens;
29 $waitSeconds = $deficit / $this->refillPerSecond;
30 usleep((int) ceil($waitSeconds * 1_000_000));
31 }
32 }
33
34 private function refill(): void
35 {
36 $now = microtime(true);
37 $elapsed = $now - $this->lastRefill;
38
39 if ($elapsed <= 0) {
40 return;
41 }
42
43 $this->tokens = min($this->capacity, $this->tokens + $elapsed * $this->refillPerSecond);
44 $this->lastRefill = $now;
45 }
46}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A token bucket allows short bursts up to capacity while enforcing a steady average rate over time.
- 2Refilling lazily from elapsed time avoids running a background timer to add tokens.
- 3Computing the wait from the exact token deficit lets a blocked caller sleep just long enough instead of busy-polling.
Related explainers
python
import time from concurrent.futures import ThreadPoolExecutor, as_completed from pathlib import Path
Parallel file downloads with a thread pool
concurrency
thread-pool
streaming-io
Intermediate
8 steps
php
use Illuminate\Support\Facades\Route; use App\Http\Controllers\Admin\DashboardController; use App\Http\Controllers\Admin\UserController; use App\Http\Controllers\Admin\ReportController;
How nested route groups compose in Laravel
routing
middleware
authorization
Intermediate
8 steps
java
public class EventBus { private final Map<Class<?>, List<Subscriber>> subscribers = new ConcurrentHashMap<>(); private final Executor executor;
Building an annotation-driven EventBus in Java
publish-subscribe
reflection
annotations
Intermediate
7 steps
javascript
import { useRef, useCallback, useEffect, useState } from "react"; function useThrottle(callback, delay) { const lastRun = useRef(0);
Building a throttle hook in React
throttling
custom-hooks
refs
Intermediate
8 steps
java
@Component public class RateLimitingFilter extends OncePerRequestFilter { private static final int MAX_REQUESTS = 100;
How a rate-limiting filter works in Spring
rate limiting
concurrency
servlet filter
Advanced
8 steps
java
public final class RetryExecutor { private final int maxAttempts; private final Duration initialDelay;
Exponential backoff retry in Java
retry
exponential-backoff
jitter
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/how-a-token-bucket-rate-limiter-works-explained-php-2aad/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.