Rate limiter in Go

2 min read Tweet this post

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:

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.

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.

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:

package main

import (
	"fmt"
	"sync"
	"time"
)

type fixedWindowRateLimiter struct {
	mu       sync.Mutex
	capacity int
	count    int
	window   time.Duration
	lastTime time.Time
}

func (l *fixedWindowRateLimiter) Allow() bool {
	l.mu.Lock()
	defer l.mu.Unlock()

	now := time.Now()
	elapsedTime := now.Sub(l.lastTime)

	if elapsedTime > l.window {
		l.count = 0
		l.lastTime = now
	}

	if l.count < l.capacity {
		l.count++
		return true
	}
	return false
}

func main() {
	limiter := &fixedWindowRateLimiter{
		capacity: 5,
		window:   time.Second,
		lastTime: time.Now(),
	}

	for i := 0; i < 10; i++ {
		if limiter.Allow() {
			fmt.Println("Allowing request")
		} else {
			fmt.Println("Rate limit exceeded")
		}
		time.Sleep(100 * time.Millisecond)
	}
}

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

programming go golang101