Redis en Spring Framework


En este artículo vamos a analizar Redis, el cual es un proyecto Open Source, para poder utilizarlo en Spring Framework.

Redis en Spring Framework
Redis

¿Qué es Redis?

Haciendo uso de la propia definición de la página oficial de Redis:

Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache and message broker.

Redis.io

Básicamente Redis es un proyecto Open Source, que puede ser usado como una cache, base de datos o un broker, el cual almacena la información en memoria.

Al almacenar los datos en memoria, nos aseguramos poder obtener la información con gran rapidez, por lo que nos da muy buen performance.

¿Cómo usar Redis en un proyecto con Spring?

En este artículo vamos a ver como podemos usar Redis en una arquitectura con Spring Cloud. Tal y como hemos comentado, vamos a probar como usar Redis de las siguientes maneras:

  • Base de Datos
  • Como sistema de mensajería
  • Como cache

Librería necesarias

Para poder usar Redis en Spring serán necesario las siguientes dependencias en tu proyecto maven:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

Redis como Base de Datos

El principal propósito de Redis es usarlo como cache en memoria o clave valor. Aunque también podemos hacer que funcione como base de datos en modo persistido.

Antes de nada vamos a levantar un contenedor de Redis:

$ docker run -d --name redis -p 6379:6379 redis redis-server --appendonly yes

Con Spring Data la implementación se hace bastante sencilla.

@RedisHash("user")
public class User {

  @Id String id;
  String firstname;
  String lastname;
  Address address;
}

Las dos anotaciones que vemos aquí tanto @Id como @RedisHash, serán las responsables de crear la clave para persistir el hash.

Para activar el funcionamiento con redis, necesitamos añadir @EnableRedisRepositories, para hacer uso de Spring Repositories.

@Configuration
@EnableRedisRepositories
public class ManagementPeople {
}

Gracias a Spring Data, evitaremos tener que crear queries y Spring las hará por nosotros.

Ahora que hemos activado el uso de Redis con los repositorios, tenemos que crear la interfaz de CrudRepository

public interface UserRepository extends CrudRepository<User, String> {

}

De esta manera podríamos persistir con Redis a través de Spring.

Redis como sistema de Mensajería

Para hacer uso de una arquitectura en la que se aplique Event Driven, se hará necesario un sistema de mensajería. Para este tipo de escenarios, en los que se quiera enviar mensajes de manera asíncrona, podemos hacer uso de Redis.

Al utilizar redis como sistema de mensajería lo primero es declaramos el Bean de una clase propia que es MessageConfiguration y ChannelTopic donde definimos el Topic, el bean TripPublisher enviara mensajes al topic definido:

@Configuration
public class RedisMessageConfiguration {
    @Autowired
    RedisTemplate<?, ?> redisTemplate;
    @Bean
    MessageConfiguration messageConfiguration() {
        return new MessageConfiguration(redisTemplate, topic());
    }
    @Bean
    ChannelTopic topic() {
        return new ChannelTopic("message");
    }
}

MessageConfiguration usa RedisTemplate para enviar mensajes al topic definido. Pero antes de enviarlo, lo convierte JSON.

@Slf4j
public class MessageConfiguration {
    RedisTemplate<?, ?> redisTemplate;
    ChannelTopic topic;
   
 public MessageConfiguration(RedisTemplate<?, ?> redisTemplate, ChannelTopic topic) {
        this.redisTemplate = redisTemplate;
        this.redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Message.class));
        this.topic = topic;
    }
    public void publish(Message message) throws JsonProcessingException {
        log.info("Sending: {}", message);
        redisTemplate.convertAndSend(topic.getTopic(), message);
    }
}

En la clase anterior hemos utilizado Jackson2JsonRedisSerializer para serializar el mensaje a JSON y luego mediante redisTemplate lo envíamos. Como apunte adicional, hemos usado @Slf4j que pertenece a Lombok, que nos ayuda a eliminar mucho boilerplate de Java. En este caso mediante esa anotación podemos hacer uso de log.

Ahora mismo ya tendríamos definido lo necesario en la parte productora, ahora faltaría la parte consumidora.

Lo principal será definir un bean RedisMessageListenerContainer, para escuchar el mensaje y estar suscrito al topic:

@Configuration
public class MessageConfiguration {

    @Autowired
    RedisConnectionFactory redisConnectionFactory;

    @Bean
    RedisMessageListenerContainer container() {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.addMessageListener(messageListener(), topic());
        container.setConnectionFactory(redisConnectionFactory);
        return container;
    }
    @Bean
    MessageListenerAdapter messageListener() {
        return new MessageListenerAdapter(new MessageSubscriber());
    }
    @Bean
    ChannelTopic topic() {
        return new ChannelTopic("message");
    }
}

A continuación crearemos una clase que será la encargada de tratar cada mensaje que llegue, para ello implementaremos de MessageListener. Cada mensaje llega serializado a JSON, por lo que habrá que convertirlo al objeto deseado.

@Service
@Slf4j
public class MessageSubscriber implements MessageListener {
    
    ObjectMapper mapper = new ObjectMapper();

    @Override
    public void onMessage(Message message, byte[] bytes) {
        try {
            log.info("Message received {}", message.toString());
            Message message = mapper.readValue(message.getBody(), Message.class);
            }
        } catch (IOException e) {
            log.error("Something went wrong {}", e.getMessage);
        }
    }
}

En esta última clase, hemos convertido el mensaje que ha llegado en formato JSON a la clase Message.

Redis como Cache

Como hemos visto Redis puede tener más usos que únicamente de Cache, ahora vamos a ver como implementarlo para que sea usado como una Cache.

Al meter la dependencia de Redis, se proporciona autoconfiguración para Redis y para la Cache.

Si se quiere modificar los valores de RedisCacheManager, puedes modificarlo poniendo el prefijo de spring.redis.*  y su propiedad:

spring.redis.host=my-redis  
spring.redis.timeout=1000

Para modificar los valores de la cache, se puede hacer mediante el prefijo spring.cache.redis.* y poniendo su propiedad.

spring.cache.redis.key-prefix=hellokoding::  
spring.cache.redis.time-to-live=100000

La información es guardada en la cache como clave valor. Y se puede utilizar la clase GenericJackson2JsonRedisSerializer, para serializar los objetos.

@RequiredArgsConstructor
@Slf4j
@Service
public class CentreService {

  private final CentreRepository centreRepository;

  @Override
  @Cacheable(value = "centres", unless = "#result.length()<1")
  public Set<Centre> getCentres() {

    log.debug("Getting centres  {} ";

    return centreRepository.getCentres();

    }
  }

Con esto ya estaríamos cacheando una lista de centros. La anotación @Cacheable lo que nos hace es cachear un método, el cual siempre tiene que ser public. Sobre la cache en Spring ya hablamos aquí. Dentro de la anotación @Cacheable, podemos definir la key o una condición para que no cache, en este caso si la lista es 0 no cacheamos nada. Como comentario, en esta clase, hemos vuelto a hacer uso de Lombok para hacer inyección de dependencias con constructor.

Aunque ya tenemos lo principal para que la cache funcione, faltaría lo más importante, activarla. Para activar la cache sería suficiente con añadir @EnableCaching.

@SpringBootApplication
@EnableCaching
public class Application {  
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Conclusión

En en esta entrada de refactorizando, hemos visto los principales usos que se suele dar a Redis, y como utilizarlo junto con el el framework de Spring Boot.


Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *