package main import ( "fmt" "sync" "time" ) // apiCall simulates an external API request func apiCall(requestID int) string { fmt.Printf("[%s] Making API call for request %d ", time.Now().Format("15:04:05.000"), requestID) time.Sleep(time.Millisecond * 100) // Simulate network latency/API processing time return fmt.Sprintf("Response for request %d", requestID) } // NewRateLimiter creates a new rate limiter that allows a burst of 'burst' requests // and then limits requests to 'rate' per 'period'. func NewRateLimiter(rate int, period time.Duration, burst int) chan struct{} { limiter := make(chan struct{}, burst) // Fill the limiter initially to allow for burst for i := 0; i < burst; i++ { limiter <- struct{}{} } // Start a goroutine to refill the limiter at the specified rate go func() { ticker := time.NewTicker(period / time.Duration(rate)) defer ticker.Stop() for range ticker.C { select { case limiter <- struct{}{}: // Token added default: // Limiter is full (burst capacity reached), do nothing } } }() return limiter } func main() { // Configure rate limiter: 5 requests per second, with a burst of 10 rateLimiter := NewRateLimiter(5, time.Second, 10) const numRequests = 20 var wg sync.WaitGroup fmt.Printf("Attempting to make %d API requests with a rate limit of 5/s and burst of 10. ", numRequests) for i := 1; i <= numRequests; i++ { wg.Add(1) go func(reqID int) { defer wg.Done() // Block until a token is available <-rateLimiter response := apiCall(reqID) fmt.Printf("[%s] Received: %s ", time.Now().Format("15:04:05.000"), response) }(i) } wg.Wait() fmt.Println("All requests completed.") }