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
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,
- 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}
- 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.
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.
- By
checking logs. There should not be any error when RedisAutoConfiguration
takes place.
- 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