Go by Example: Rate Limiting

Rate limiting is a technique for controlling the rate at which events occur in a system. In Go, it can be used to control the rate at which network requests are made, or the rate at which a function is called.

Here’s an example of rate limiting using a time.Ticker:

package main

import (
	"fmt"
	"time"
)

func main() {
	requests := make(chan int, 5)
	for i := 1; i <= 5; i++ {
		requests <- i
	}
	close(requests)

	limiter := time.Tick(200 * time.Millisecond)

	for req := range requests {
		<-limiter
		fmt.Println("request", req, time.Now())
	}

	burstyLimiter := make(chan time.Time, 3)

	for i := 0; i < 3; i++ {
		burstyLimiter <- time.Now()
	}

	go func() {
		for t := range time.Tick(200 * time.Millisecond) {
			burstyLimiter <- t
		}
	}()

	burstyRequests := make(chan int, 5)
	for i := 1; i <= 5; i++ {
		burstyRequests <- i
	}
	close(burstyRequests)

	for req := range burstyRequests {
		<-burstyLimiter
		fmt.Println("request", req, time.Now())
	}
}

In this example, there are two rate limiters: a simple limiter and a bursty limiter. The simple limiter uses a time.Tick to limit the rate at which events occur to once every 200 milliseconds. The bursty limiter allows bursts of up to 3 events before rate limiting. A Goroutine is used to continuously fill the bursty limiter channel with events, allowing bursty events to occur. In both cases, incoming requests are blocked until an event is available in the limiter channel.

Here’s another example of rate limiting using a token bucket algorithm:

package main

import (
	"fmt"
	"time"
)

func rateLimiter(bucket chan struct{}, burstSize int, fillInterval time.Duration) {
	for i := 0; i < burstSize; i++ {
		bucket <- struct{}{}
	}

	ticker := time.Tick(fillInterval)
	for range ticker {
		select {
		case bucket <- struct{}{}:
		default:
		}
	}
}

func main() {
	bucket := make(chan struct{}, 5)
	go rateLimiter(bucket, 5, 200*time.Millisecond)

	requests := make(chan int, 20)
	for i := 1; i <= 20; i++ {
		requests <- i
	}
	close(requests)

	for req := range requests {
		<-bucket
		fmt.Println("request", req, time.Now())
	}
}

In this example, a token bucket is used to limit the rate at which events occur. A Goroutine is used to fill the bucket with tokens at a specified interval, and incoming requests are blocked until a token is available in the bucket. The burst size determines the maximum number of events that can occur before rate limiting begins, and the fill interval determines the rate at which tokens are added to the bucket.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *