Como implementar Spring Cache com Redis

Nesse artigo vamos abordar, como configurar devidamente seu sistema de cache, usando o Spring Cache, e utilizá-lo junto com o banco de dados NoSQL Redis, para que seu cache funcione independente de quantas instâncias de sua aplicação estão executando.

Motivação

Quando construímos sistemas que possuem um número de requisições para consultas alto, e que as informações não se alteram por um período considerável, é interessante implementar um sistema de cache para que o número excessivo de requisições não derrube toda a sua infraestrutura, e quando temos aplicações que podem ser escaláveis horizontalmente, ou seja mais de uma instancia executando em paralelo é importante centralizar esse cache para que ele funcione no sistema com um todo e não só quando for feita a requisição para uma instancia especifica.

Implementação

Vamos imaginar que você já tenha sua aplicação funcionando e que vamos adicionar apenas a funcionalidade de cache.

Para isso vamos precisar adicionar duas dependências na sua aplicação:

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-cache</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
pom.xml

Nosso próximo passo é configurar a aplicação para conseguir se conectar ao Redis, para isso vamos alterar o nosso arquivo application.properties e adicionar as informações de conexão.

spring.cache.type=redis
spring.redis.host=<host>
spring.redis.port=<porta>
spring.redis.password=<senha-do-banco>
application.properties

Agora precisamos configurar a integração do Spring Cache com o Banco Redis, para isso vamos criar uma classe de configuração, para lidar com essa integração:

package com.artefatox.config;

import java.time.Duration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair;

@Configuration
public class RedisConfiguration {

	@Bean
	RedisCacheConfiguration cacheConfiguration() {
		return RedisCacheConfiguration
				.defaultCacheConfig()
				.entryTtl(Duration.ofMinutes(5L)).disableCachingNullValues()
				.serializeValuesWith(SerializationPair
						.fromSerializer(new GenericJackson2JsonRedisSerializer()));
	}
}
RedisConfiguration.java

Agora basta anotar os métodos das classes de serviço para gerenciar o cache por nós, segue um exemplo:

package com.artefatox.service;

import java.util.List;

import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import com.artefatox.cloud.Example;
import com.artefatox.cloud.ExampleRepository;
import com.artefatox.cloud.Pageable;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@AllArgsConstructor
public class ExampleService {

	private final ExampleRepository repository;
	
	@Cacheable("example")
	public List<Example> list(Pageable pageable) {
		return repository.findAll();
	}
	
	@Cacheable(value = "example", key = "#id")
	public Example getById(Long id) {
		return repository.findById(id);
	}
	
	@CacheEvict(cacheNames = "example", allEntries = true)
	public void noCache() {
		log.info("Cache evict");
	}
}
ExampleService.java

Pronto agora os seus métodos estão com o cache, habilitado, para testar tente fazer requisições e acompanhe os logs no terminal, verá que quando o resultado da consulta é feito por mais de uma vez, a resposta é entregue, sem chamar todos os métodos, assim como se você tem esse método conectado a um banco de dados, verá que o banco de dados não será chamado.

Explicando as annotations do Spring Cache

  • @Cacheable: Essa anotação indica que o método deve ser armazenado em cache, quando esse método for chamado, o Spring por baixo dos panos verifica se essa chamada está armazenada em cache com base nos parâmetros passados, caso esteja retorna as informação contidas no cache caso contrário ele irá seguir com a lógica por completo, realizando consultas em bancos, caso esse seja o destino do método. Essa anotação ainda te permite personalizar a chave que será usada para armazenar e recuperar os dados do cache, no exemplo acima como temos um método que utiliza uma busca pro id, utilizamos o mesmo para especificar a consulta.
  • @CacheEvict: Essa anotação é utilizada para remover o cache armazenado, podemos passar como parâmetro o nome dos caches, para que quando o método anotado com ela for chamado, a exclusão do cache aconteça. Ainda podemos utilizar alguns parâmetros para controlar melhor essa exclusão como é o caso do allEntries, quando definimos como true, ele remove todos os dados associados pelo cache, ou ainda podemos passar uma key específica, para que a exclusão só aconteça para aquela chave.

Conclusão

Implementar um sistema de cache eficiente em aplicações é sempre muito importante, pois reduz o uso de recursos, fazendo com que a solução fiquei mais barata de se manter, e apresente uma performance desejada.
O uso do cache com Redis é bem interessante pelo fato do Redis ser um banco não relacional e em memória, sua leitura e escrita são muito rápidas, o que contribui e muito para a performance da aplicação.
De fato essa configuração acaba sendo muito útil e muito fácil de implementar, pois o ecossistema do Spring já nos entrega muita coisa pronta.

Links úteis

Mauricio Lima
Mauricio Lima

Bacharel em Ciência da Computação, profissional dedicado ao desenvolvimento de software e entusiasta da tecnologia.

Artigos: 65