Published on

Rate limiter in Go

Authors

A rate limiter is a tool or mechanism that is used to limit the rate at which a system or service can be accessed or used. Rate limiters are often used to prevent overloading or abuse of a system, by limiting the number of requests or the rate at which requests can be made.

There are many ways to implement a rate limiter, and the specific approach you should use will depend on the requirements of your application. Some common approaches include:

Token bucket

In a token bucket system, the rate limiter maintains a bucket of tokens, and allows requests to be made as long as there are enough tokens in the bucket. The rate at which tokens are added to the bucket is controlled by a rate parameter, and the rate at which requests can be made is limited by the number of tokens in the bucket.

Sliding window

A sliding window approach is similar to a fixed window approach, but the time window is continuously moving forward in time. This allows the rate limiter to adapt to changes in the rate of requests over time, and to provide more fine-grained control over the rate at which requests can be made.

Fixed window

In a fixed window approach, the rate limiter maintains a count of the number of requests made within a fixed time window, and allows requests to be made as long as the count is below a specified threshold. The rate at which requests can be made is limited by the size of the time window and the threshold.

Here is an example of how you can implement a fixed window rate limiter in Go:

fixedwindow.go

_51
package main
_51
_51
import (
_51
"fmt"
_51
"sync"
_51
"time"
_51
)
_51
_51
type fixedWindowRateLimiter struct {
_51
mu sync.Mutex
_51
capacity int
_51
count int
_51
window time.Duration
_51
lastTime time.Time
_51
}
_51
_51
func (l *fixedWindowRateLimiter) Allow() bool {
_51
l.mu.Lock()
_51
defer l.mu.Unlock()
_51
_51
now := time.Now()
_51
elapsedTime := now.Sub(l.lastTime)
_51
_51
if elapsedTime > l.window {
_51
l.count = 0
_51
l.lastTime = now
_51
}
_51
_51
if l.count < l.capacity {
_51
l.count++
_51
return true
_51
}
_51
return false
_51
}
_51
_51
func main() {
_51
limiter := &fixedWindowRateLimiter{
_51
capacity: 5,
_51
window: time.Second,
_51
lastTime: time.Now(),
_51
}
_51
_51
for i := 0; i < 10; i++ {
_51
if limiter.Allow() {
_51
fmt.Println("Allowing request")
_51
} else {
_51
fmt.Println("Rate limit exceeded")
_51
}
_51
time.Sleep(100 * time.Millisecond)
_51
}
_51
}

try in playground

In this example, the fixedWindowRateLimiter struct stores the capacity of the rate limiter, the duration of the time window, the current count of requests within the window, and the time of the last request. The Allow method is used to try to allow a request. It checks the elapsed time since the last request and resets the count if the elapsed time is greater than the window duration. If the count is below the capacity, it increments the count and returns true, otherwise it will return false.

Rate limiters can be implemented in a variety of ways, including using software libraries or frameworks, hardware appliances, or cloud-based services. They are commonly used in web applications, API servers, and other systems that handle a high volume of requests, to ensure that the system can handle the load and provide a consistent level of service to users.

For details implementation of token bucket and sliding window will be covered in the next post