Friday, February 11, 2022

Spring Boot Redis Cache Example

 

Spring Boot Redis Cache Example  

We discussed how to enable caching implementation in Spring Boot using an in-memory cache manager. But, there were a few drawbacks to that approach. This is where the Redis cache store comes into the picture.

Introduction

Redis is a high-performance datastore with high read/write throughput. This is why it is a perfect candidate for storing cache and session information.

Setup Local Server for Redis

Note: If you already have a Redis server setup or you are familiar with these steps, you can skip to Adding Redis Dependencies.

The best way to set up a Redis server in the local machine is to use docker. In this case, I’m using Redis along with Redis-commander GUI for Redis.

In application.yaml or application.properties
version: '3'

services:

  redis:

    container_name: redis

    hostname: redis

    image: redis     ports:

      - "6379:6379"

  redis-commander:

    container_name: redis-commander

    hostname: redis-commander

    image: rediscommander/redis-commander:latest

    restart: always

    environment:

      - REDIS_HOSTS=local:redis:6379

    ports:

      - "8081:8081"

The above step is only for testing things locally. For production, please follow official Redis documentation for the server installation.

Run the following command for starting the containers.

docker-compose docker-compose.yml up -d

Once the docker container is up, You can view Redis commander at http://localhost:8081

Redis commander UI to view cache

Add redis dependencies

Like always, Spring boot makes things easier with the help of sophisticated starter dependencies. All you have to do is to add the Redis starter.

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-data-redis</artifactId>

</dependency>

Add appropriate settings for the Redis configuration. You can skip the following if you are using Redis locally.

spring.redis.host=localhost

spring.redis.port=6379

In production you may need to add spring.redis.username and spring.redis.password based on your server.

Enable Caching

To enable caching support in Spring boot, first, you need to annotate the main class with @EnableCaching

@EnableCaching

@SpringBootApplication

public class SpringBootRedisCacheExampleApplication {

    public static void main(String[] args) {

        SpringApplication.run(SpringBootRedisCacheExampleApplication.class, args);

    }

}

Add @Cacheable annotation

Find a long-running method and annotate it with @Cacheable. This annotation takes a value which is the cache name. And the key is a unique object to look up the value in cache later.

@Cacheable(value = "items", key = "#id")
public Item getItem(Integer id) {
    Item item = itemRepository.findById(id).orElseThrow(RuntimeException::new);
    logger.info("Loading data from DB {}", item);
    return item;
}

 

In this case, We used Spring Expression Language(SpEL) to pass the argument value as key

Add @CacheEvict annotation

Once cached, The values stay there indefinitely. Thus, the cache abstraction will never pick updated values from the database. For this reason, you should use @CacheEvict on updates.

@CacheEvict(value = "items", key = "#id")

public Item updateItem(Integer id, Item request) {

    Item item = getItem(id);

    item.setPrice(request.getPrice());

    item.setProductName(request.getProductName());

    return itemRepository.save(item);

}

See Redis in Action

Once we start the application, The first request goes to the database. And the subsequent requests fetch data from the Redis cache-store. We know this because,

  1. The logs says the records is from the database.

c.s.e.s.cache.service.ItemService:Loading data from DB Item{id=2,productName='Pants Large',price=21.99}

  1. When we open redis-commander, we can see that items cache containing entry with key 2 .These cached value is in the form of binary.

Redis commander showing cached values from Spring Boot

What about the cache evict? Let’s try updating item 2.

curl -X PUT \

  http://localhost:8080/items/2 \

  -H 'cache-control: no-cache' \

  -H 'content-type: application/json' \

  -d '{     "productName": "Pants Large",

    "price": 14.99

}'

HTTP/1.1 200

Content-Type: application/json

Transfer-Encoding: chunked

Date: Sun, 13 Dec 2020 18:11:16 GMT

{"id":2,"productName":"Pants Large","price":14.99}

As you see the cached value for key 2 is now gone. The next time a getItem call happens, the values will be cached again.

Caching between multiple application instances

As Redis server runs outside the application, multiple application instances can share the same cache-store. For example, Let’s run the same application again on different port 7070 (using —server.port=7070).

java -jar redis-demo.jar --server.port=7070

We can confirm that the second instance had successfully connected to the redis server by various means.

  1. By checking logs. There should not be any error when RedisAutoConfiguration takes place.
  2. By checking the client connections on the server. Click the local (redis:6379:0) element at the side nav of redis-commander, and it will give the server stats. In the results, find the value for Connected clients. The value should be 3. This is due to 2 connections from the application and 1 connection from the redis-commander itself.

Let’s call the API from application running on port 8080.

curl -X GET http://localhost:8080/items/2

This creates a cache entry in redis. Now try hitting server on 7070 for the same resource

curl -X GET http://localhost:7070/items/2

The response is immediate and there is no logs that says the record is from the database.

Timeout for cached values

You can also specify how long the value can live in the cache store.

spring.cache.redis.time-to-live=5m

Summary

To summarize, We learned how to use redis data store as cache for spring boot application. The code and the docker-compose.yml for this example is available in this github repository.

 

 

No comments:

Post a Comment

All Java 8 / J2EE Interview for Experienced

  1.       Functional Interface, Stream API? Functional interfaces are also called Single Abstract Method interfaces (SAM Interfaces). As ...