Rate Limiting HTTP Requests in Go based on IP address
If you are running HTTP server and want to rate limit requests to the endpoints, you can use well-maintained tools such as github.com/didip/tollbooth. But if you’re building something very simple, it’s not that hard to implement it on your own.
There is already an experimental Go package x/time/rate
, which we can use.
In this tutorial, we’ll create a simple middleware for rate limiting based on the user’s IP address.
Pure HTTP Server
Let’s start with building a simple HTTP server, that has very simple endpoint. It could be a heavy endpoint, that’s why we want to add a rate limit there.
|
|
In main.go
we start the server on :8888
and have a single endpoint /
.
golang.org/x/time/rate
We will use x/time/rate
Go package which provides a token bucket rate-limiter algorithm. rate#Limiter controls how frequently events are allowed to happen. It implements a “token bucket” of size b
, initially full and refilled at rate r
tokens per second. Informally, in any large enough time interval, the Limiter limits the rate to r tokens per second, with a maximum burst size of b events.
Since we want to implement rate limiter per IP address, we will also need to maintain a map of limiters.
|
|
NewIPRateLimiter
creates an instance of IP limiter, and HTTP server will have to call GetLimiter
of this instance to get limiter for the specified IP (from the map or generate a new one).
Middleware
Let’s upgrade our HTTP Server and add middleware to all endpoints, so if IP has reached limit it will respond 429 Too Many Requests, otherwise, it will proceed with the request.
In the limitMiddleware
function we call the global limiter’s Allow()
method each time the middleware receives an HTTP request. If there are no tokens left in the bucket Allow()
will return false and we send the user a 429 Too Many Requests response. Otherwise, calling Allow()
will consume exactly one token from the bucket and we pass on control to the next handler in the chain.
|
|
Build & Run
|
|
Test
There is one very nice tool I like to use for HTTP load testing, called vegeta (which is also written in Go).
|
|
We need to create a simple config file saying what requests do we want to produce.
|
|
And then run attack for 10 seconds with 100 requests per time unit.
|
|
As a result you will see that some requests returned 200, but most of them returned 429.