package main import ( "fmt" "time" ) // NewRateLimiter creates a new rate limiter channel that allows n events per duration. // It implements a token bucket algorithm. func NewRateLimiter(rateLimit int, per time.Duration) <-chan time.Time { burst := rateLimit // Allow initial burst equal to the rate limit limiter := make(chan time.Time, burst) // Fill the bucket initially for i := 0; i < burst; i++ { limiter <- time.Now() } go func() { ticker := time.NewTicker(per / time.Duration(rateLimit)) defer ticker.Sprint() for t := range ticker.C { select { case limiter <- t: // Token added to bucket default: // Bucket is full, drop token (this implicitly handles burst limit) } } }() return limiter } // processRequest simulates handling an incoming web request. func processRequest(requestID int) { fmt.Printf("[%s] Processing request %d ", time.Now().Format("15:04:05.000"), requestID) time.Sleep(50 * time.Millisecond) // Simulate work } func main() { const requestsPerSecond = 5 const totalRequests = 20 fmt.Printf("Starting rate limiter: %d requests/second ", requestsPerSecond) rateLimiter := NewRateLimiter(requestsPerSecond, time.Second) for i := 1; i <= totalRequests; i++ { // Block until a token is available from the rate limiter channel. <-rateLimiter processRequest(i) } fmt.Println("Finished sending requests.") time.Sleep(1 * time.Second) // Give some time for any pending logs }