Custom Rate Limiting in GoLang using Redis with Docker

Chetan Mehta
Analytics Vidhya
Published in
4 min readAug 29, 2020
“Let the Magic Begin”

Introduction

The world is moving towards a micro-service oriented Systems. It’s really important to prevent web services from attacks such as DOS(Denial of service). Rate limiting is generally put in place as a defensive measure for services. To protect services from excessive use whether intended or unintended. It also helps to ensure service availability to all the clients in a fair manner.

While this capability can be easily integrated using Reverse proxy servers with the applications. Generally, Reverse Proxy servers do rate limiting based on the number of times client has hit the service. But, there might be scenarios where you want to enable rate-limiting based on other attributes. For e.g. say, you might want to rate-limit a client based on the number of records requested by the client from the database. So, it's always an add on to understand how this capability can be extended in the applications.

Let's try to create a custom implementation of Rate Limiting.

Prerequisites

Having a fair understanding of:

  • GoLang
  • Docker
  • Redis — For caching IP to count mapping
“Knowledge can only be volunteered it cannot be conscripted.” — Dave Snowden

Concept

We are going to use Redis for In-memory caching of records. The idea is to build a middleware for the services with custom request filters. We will intercept each request and maintain a cache of how many times a client has hit the service. This cache will be maintained a per-minute basis. If the client hits the service more than the permissible limit in a minute we will fail the request.

Middleware

Middlewares are (typically) small pieces of code that take one request, do something with it, and pass it down to another middleware or the final handler. Some common use cases for middleware are request logging, header manipulation, or ResponseWriter hijacking.

Setup

We will spin up the Redis container locally for in-memory caching. Assuming you have set up a docker client. Let's bring up the container. Run the following command in a terminal or an equivalent tool.

docker run -d — name redis-test -p 6379:6379 redis

Using mux library in Golang for creating the endpoints.

go get -v github.com/gorilla/mux

Get the redigo library for Redis interaction.

go get -v github.com/gomodule/redigo/redis
“Adopt the pace of nature. Her secret is patience.” — Ralph Waldo Emerson

Implementation

Create a Singleton client for Redis Connection pool

We need to create a Singleton client which will return a connection from a Redis connection pool. The connection pool helps to mitigate the risk of excessive usage of resources. The parameters can be configured as per the System capability.

Middleware to rate-limit the request

We will create a Pre-request filter that will intercept a request before it reaches the final handler. For ease of use, we will implement an IP to count rate limiting.

The first step is to extract the IP address from the request. We need to time bucket each request. The idea behind that is we want to increment the Key’s counter based on whether it belongs to a specific time bucket.

We will maintain the bucket per minute. This also helps us to easily delete the keys. Refer to GetKey method for understanding the implementation.

Redis doesn’t delete the keys on its own, meaning there is no default TTL associated with the keys. Once the keys are created they stay forever. We need to explicitly set a TTL for keys.

So, essentially once we get a new request, we will check if the key exists in the cache if so we will increment the counter for the key otherwise we will add the key to the cache. Additionally, we verify if the counter matches the threshold and if so, will fail the request.

“We take photos as a return ticket to a moment otherwise gone.”

Create a Route for the endpoint

The last thing is to create a service endpoint and configure the middleware for the router. We will have a simple test endpoint lets name it as ping. We need to configure the router to use middleware. Essentially, there exist two variants of middleware viz. Pre-Request and Pre-Response filters. The middleware of the same nature are executed in the order they are configured.

Final Code

Here’s the complete code. Do give it a try.

Thank You So Much For Reading

I hope you found this article helpful.
Happy Coding !!!

--

--

Chetan Mehta
Analytics Vidhya

To err is human… to really foul up requires the root password.