In this article, we are going to build a Rate Limiter middleware for a Gin server.
We will build a very simple rate limiter, one that stops accepting requests when these exceed the limit in a certain time frame. We Will not create a rate limiter that keeps track of the IP Addresses and stops them from making too many requests.
Rate Limiter
According to this article, published by CloudFlare, a rate limiter is a strategy for limiting network traffic. It puts a cap on how often someone can repeat an action within a certain timeframe for instance, trying to log in to an account. Rate limiting can help stop certain kinds of malicious bot activity. It can also reduce strain on web servers.
Rate limiting is often employed to stop bad bots from negatively impacting a website or application. Bot attacks that rate limiting can help mitigate include Brute force attacks, DoS and DDoS attacks and Web scraping.
Rate limiting also protects against API overuse, which is not necessarily malicious or due to bot activity, but is important to prevent nonetheless.
Requirements
- Go installed
Rate Limiter Algorithms
Fixed window algorithm: This algorithm maintains a counter of the number of requests made within a fixed time window, such as one minute. If the counter exceeds the rate limit, subsequent requests are rejected.
Sliding window algorithm: This algorithm is similar to the fixed window algorithm, but the time window slides forward as new requests are received. This means that requests that were made earlier in the window may still be counted against the rate limit, even if they are no longer within the current window.
Leaky bucket algorithm: This algorithm maintains a bucket that fills up at a constant rate. When a request is received, it is allowed to pass through the bucket if there is space available. If the bucket is full, the request is rejected.
Token bucket algorithm: This algorithm is similar to the leaky bucket algorithm, but instead of a bucket, it maintains a counter of tokens. Tokens are added to the counter at a constant rate, and requests are allowed to pass through if there is at least one token available. If the counter is empty, requests are rejected.
Simple Rate Limiter
We created a new Go project, RateLimiter. And installed Gin.
$ go get -u github.com/gin-gonic/gin
For this rate limiter we are going to use the rate package. Which implements the token bucket algorithm.
And in the main.go
file, we start creating a handler that will be used to limit the number of requests made to the server.
main.go
package main
import (
"github.com/gin-gonic/gin"
"golang.org/x/time/rate"
)
func RateLimiter() gin.HandlerFunc {
limiter := rate.NewLimiter(1, 4)
return func(c *gin.Context) {
if limiter.Allow() {
c.Next()
} else {
c.JSON(http.StatusTooManyRequests, gin.H{
"message": "Limite exceed",
})
}
}
This is the RateLimiter()
function, we create a NewLimiter instance that allows up to 4 requests per second. According to the documentation, the NewLimiter function returns a new Limiter, func NewLimiter(r Limit, b int) *Limiter
and a Limiter controls how frequently events are allowed to happen. It implements a "token bucket" of size b
, initially full and refilled at the rate of r
tokens per second.
The zero value is a valid Limiter, but it will reject all events. Use NewLimiter to create non-zero Limiters.
The Allow()
method of the rate limiter returns a boolean value indicating whether the request is allowed. If the request is allowed, the Next()
method of the gin.Context
object is called to continue processing the request. Otherwise, the JSON()
method of the gin.Context
object is called to send a response with the status code 429
(Too Many Requests).
func main() {
r := gin.Default()
r.Use(RateLimiter())
r.GET("/", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
In the main()
function, we create a Gin instance and use the Use()
function to register the RateLimiter()
middleware.
The RateLimiter()
handler is called every time a request is made to the server.
In our command line, we run this file using go run main.go
command.
To test this rate limiter, we created another project, TestLimiter
. And use the following code in its main.go
file.
package main
import (
"fmt"
"io/ioutil"
"net/http"
"time"
)
func main() {
c := http.Client{Timeout: time.Duration(1) * time.Second}
limit := 20
for i := 1; i < limite; i++ {
resp, err := c.Get("http://localhost:8080/")
if err != nil {
fmt.Printf("Error %s", err)
fmt.Println("Count is: ", i)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fmt.Printf("Body : %s", body)
}
}
In this file, we define the c
variable, which is a http.Client
object that is used to make HTTP requests. The limit
variable is an integer variable that specifies the number of times the Get()
method will be called.
Then we created a loop that iterates limit
times. In each iteration of the loop, the code makes a Get()
request to the URL http://localhost:8080/
. The Get()
method returns an *http.Response
object, which contains the response from the server.
The code then checks the err
variable to see if there was an error in making the request. If there is an error, the code prints the error message and the current value of the i
variable. Otherwise, the code reads the body of the response and prints it to the console.
We run this file, using go run main.go
in another terminal.
The Rate Limiter terminal should show these messages:
And the TestLimiter
terminal should show these messages:
Conclusion
In this article, we built a rate limiter for a Gin server. Building a rate limiter for your Gin server is an important step in protecting your application from abuse. By limiting the number of requests that can be made to your server, you can help improve the performance and reliability of your application for all users.
The source code is here.
Thank you for taking the time to read this article.
If you have any recommendations about other packages, architectures, how to improve my code, my English, or anything; please leave a comment or contact me through Twitter, or LinkedIn.
Top comments (0)