Created
August 1, 2025 09:33
-
-
Save hawaijar/9c9e7ee2087821c6fdb7c5c672c287bc to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"log" | |
"time" | |
) | |
func main() { | |
// Example 1: Token Bucket (smooth rate limiting) | |
fmt.Println("=== Token Bucket Example ===") | |
// 10 tokens per second, burst capacity of 20 | |
tokenBucket := ratelimiter.NewTokenBucket(10, 20) | |
userID := "user123" | |
// Burst: Use 15 tokens quickly | |
for i := 0; i < 15; i++ { | |
allowed, _ := tokenBucket.Allow(userID) | |
fmt.Printf("Request %d: %v\n", i+1, allowed) | |
} | |
// Wait for tokens to refill | |
fmt.Println("\nWaiting 2 seconds for tokens to refill...") | |
time.Sleep(2 * time.Second) | |
// Should have ~20 tokens now (refilled at 10/sec) | |
for i := 0; i < 5; i++ { | |
allowed, _ := tokenBucket.Allow(userID) | |
fmt.Printf("Request after wait %d: %v\n", i+1, allowed) | |
} | |
// Example 2: Sliding Window Log (precise but memory intensive) | |
fmt.Println("\n\n=== Sliding Window Log Example ===") | |
// 5 requests per 10 seconds | |
swLog := ratelimiter.NewSlidingWindowLog(5, 10*time.Second) | |
// Make 5 requests (should all pass) | |
for i := 0; i < 5; i++ { | |
allowed, _ := swLog.Allow("api-key-456") | |
fmt.Printf("Request %d: %v\n", i+1, allowed) | |
time.Sleep(1 * time.Second) | |
} | |
// 6th request should fail | |
allowed, _ := swLog.Allow("api-key-456") | |
fmt.Printf("Request 6 (should fail): %v\n", allowed) | |
// Wait for oldest request to expire | |
fmt.Println("\nWaiting 6 seconds for window to slide...") | |
time.Sleep(6 * time.Second) | |
// Should allow one more request now | |
allowed, _ = swLog.Allow("api-key-456") | |
fmt.Printf("Request after wait: %v\n", allowed) | |
// Example 3: Sliding Window Counter (balanced approach) | |
fmt.Println("\n\n=== Sliding Window Counter Example ===") | |
// 100 requests per minute, with 6 sub-windows (10-second buckets) | |
swCounter := ratelimiter.NewSlidingWindowCounter(100, time.Minute, 6) | |
// Simulate API traffic | |
fmt.Println("Simulating API traffic over 30 seconds...") | |
start := time.Now() | |
allowed := 0 | |
rejected := 0 | |
for time.Since(start) < 30*time.Second { | |
if ok, _ := swCounter.Allow("service-xyz"); ok { | |
allowed++ | |
} else { | |
rejected++ | |
} | |
time.Sleep(200 * time.Millisecond) // 5 requests per second | |
} | |
fmt.Printf("Results: %d allowed, %d rejected\n", allowed, rejected) | |
fmt.Printf("Effective rate: %.2f requests/minute\n", float64(allowed)*2) | |
// Example 4: Multiple users with different patterns | |
fmt.Println("\n\n=== Multi-User Example ===") | |
limiter := ratelimiter.NewTokenBucket(5, 10) // 5 req/sec, burst of 10 | |
users := []string{"alice", "bob", "charlie"} | |
// Each user makes requests at different rates | |
for _, user := range users { | |
fmt.Printf("\nUser %s making requests:\n", user) | |
for i := 0; i < 8; i++ { | |
allowed, _ := limiter.Allow(user) | |
status := "✓ allowed" | |
if !allowed { | |
status = "✗ rejected" | |
} | |
fmt.Printf(" Request %d: %s\n", i+1, status) | |
// Different delays per user | |
if user == "alice" { | |
time.Sleep(100 * time.Millisecond) // Fast user | |
} else if user == "bob" { | |
time.Sleep(200 * time.Millisecond) // Medium user | |
} else { | |
time.Sleep(300 * time.Millisecond) // Slow user | |
} | |
} | |
} | |
// Example 5: Distributed rate limiting simulation | |
fmt.Println("\n\n=== Distributed System Example ===") | |
// Simulate 3 servers with shared rate limit | |
servers := []string{"server1", "server2", "server3"} | |
globalLimiter := ratelimiter.NewSlidingWindowLog(50, 10*time.Second) | |
// Each server processes requests for the same API key | |
apiKey := "shared-api-key" | |
requestCount := 0 | |
for i := 0; i < 60; i++ { | |
server := servers[i%len(servers)] | |
if allowed, _ := globalLimiter.Allow(apiKey); allowed { | |
requestCount++ | |
fmt.Printf("Request %d from %s: allowed (total: %d)\n", | |
i+1, server, requestCount) | |
} else { | |
fmt.Printf("Request %d from %s: RATE LIMITED\n", i+1, server) | |
} | |
time.Sleep(150 * time.Millisecond) | |
} | |
fmt.Printf("\nTotal allowed requests: %d/60 (limit was 50 per 10 seconds)\n", | |
requestCount) | |
} | |
// Helper function to demonstrate rate limiter with HTTP middleware | |
func RateLimitMiddleware(limiter ratelimiter.RateLimiter) func(handler func()) func() { | |
return func(handler func()) func() { | |
return func() { | |
// In real implementation, extract key from request (e.g., IP, API key) | |
key := "client-ip-192.168.1.1" | |
if allowed, _ := limiter.Allow(key); !allowed { | |
log.Println("Rate limit exceeded for", key) | |
// In real HTTP: return 429 Too Many Requests | |
return | |
} | |
// Process request | |
handler() | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment