Implementing Redis Caching in Go with Docker

Abinav Ghimire
readytowork, Inc.
Published in
4 min readSep 29, 2023

Caching is a crucial aspect of modern software development, helping to improve application performance and reduce latency. Although I do believe that caching might not be appropriate for all applications, it is still important to understand what caching does and why it might be an important feature in many applications.

In this article, we will use Redis, a popular in-memory data store, which is an excellent choice for implementing caching in Go applications. In this article, we will walk through the process of setting up Redis caching in a Go application using Docker, which makes it easy to manage and deploy. This article assumes that you have installed Docker and Go in your system and have a Go project already set up.

Setting up docker and config files

Firstly, we will get go-redis in our main docker file by adding RUN go get github.com/go-redis/redis. Our main docker file will look something like this:

FROM golang:1.18-alpine

WORKDIR /redis_docker

# Copy everything from this project into the filesystem of the container.
COPY . .

# Obtain the package needed to run redis commands.
RUN go get github.com/go-redis/redis

RUN go mod tidy

# Compile the binary exe for our app.
RUN go build -o main .
# Start the application.
CMD ["./main"]

As Redis is a standalone service, we need to create a Redis service from our docker-compose file to get it up and running in the docker. Under the services key, we will add the following key-values to run the Redis service and our Go application.

services:
redis:
container_name: "redis"
image: redis:alpine
# Specify the redis.conf file to use and add a password.
command: redis-server /usr/local/etc/redis/redis.conf --requirepass ${REDIS_PASSWORD}
ports:
- "6379:6379"
web:
container_name: "redisapi"
build:
# build the image using the Dockerfile we have in this project. Can use an image instead.
context: .
ports:
- "8080:8080"

Note: In the above code, you can see that REDIS_PASSWORD variable is required which should be present in the .env file.

Implementing Redis Cache

In our Go application, we need to initialize the Redis Cache service in order to use the service when required. To initialize the service, use the code snippet below:

import (
"errors"
"github.com/go-redis/redis"
)

type Redis struct {
RedisClient redis.Client
}

func NewRedis( env Env) Redis {

var client = redis.NewClient(&redis.Options{
// Container name + port since we are using docker
Addr: "redis:6379",
Password: env.RedisPassword,
})

if client == nil {
errors.New("Cannot run redis")
}

return Redis{
RedisClient: *client,
}
}

Now, we will use the above service to cache our data. To cache the data that needs to be cached, we will append the following code where needed.

Note: We have used gin framework for this article

func (cc UserController) GetUserProfile(c *gin.Context) {
// Redis Cache
//Get current URL and parse to string
endpoint := c.Request.URL

cachedKey := endpoint.String()

response := map[string]interface{}{
"data": data_that_needs_to_be_cached,
}

//Need to marshal the data that needs to be cached
mr, _ := json.Marshal(&response)

//Set cached data to a key and allocate a time for the data to be cached
cacheErr := cc.redisClient.RedisClient.Set(cachedKey, mr, 10*time.Second).Err()
if cacheErr != nil {
responses.HandleError(c, cacheErr)
return
}
c.JSON(http.StatusOK, response)
}

Now, we need to use the service to create a middleware using gin framework which will search for cached keys. The middleware will look like this:

type RedisMiddleware struct {
env infrastructure.Env
redisClient infrastructure.Redis
}

func NewRedisMiddleware(
env infrastructure.Env,
redisClient infrastructure.Redis,
) RedisMiddleware {
return RedisMiddleware{
env: env,
redisClient: redisClient,
}
}

// Verify Redis Cache
func (m RedisMiddleware) VerifyRedisCache() gin.HandlerFunc {
return func(c *gin.Context) {

//Get current URL
endpoint := c.Request.URL

//Keys are string typed
cachedKey := endpoint.String()

//Get Cached keys
val, err := m.redisClient.RedisClient.Get(cachedKey).Bytes()

//If error is nil, it means that redis cache couldn't find the key, hence we
// push on to the next middleware to keep the request running
if err != nil {
c.Next()
return
}

//Create an empty interface to unmarshal our cached keys
responseBody := map[string]interface{}{}

//Unmarshal cached key
json.Unmarshal(val, &responseBody)

c.JSON(http.StatusOK, responseBody)
// Abort other chained middlewares since we already get the response here.
c.Abort()
}
}

Finally, we will use the middleware in our routes in order to implement caching which will look like this:

r := gin.Default()
r.router.Gin.GET("/users/profile", redisMiddleware.VerifyRedisCache(), userController.GetUserProfile)

Conclusion

In this article, we’ve demonstrated how to implement Redis caching in a Go application using Docker. Caching with Redis can significantly improve the performance and responsiveness of your applications, and Docker simplifies the deployment and management of both your Go application and the Redis server. Feel free to explore more advanced use cases and integrate Redis caching into your own projects to benefit from its speed and efficiency.

--

--