Spring Boot con Redis: HashOperations CRUD Funcionalidad

Redis es un almacén de datos en memoria, a menudo utilizado para almacenamiento en caché o como base de datos. En este artículo, usando la clase HashOperations, crearemos una aplicación Redis CRUD en Java y Spring Boot.

Introducción

Servidor de diccionario remoto (Redis) es un almacén de estructura de datos en memoria. Se puede usar como una base de datos simple, un intermediario de mensajes y para el almacenamiento en caché a través de su soporte para varias estructuras de datos.

En este artículo, crearemos una aplicación CRUD simple e integraremos Redis con Spring Boot. Para lograr la funcionalidad CRUD, nos basaremos en la interfaz HashOperations proporcionada por el proyecto Redis de datos de primavera.

Redis

Redis es un almacén de datos en memoria de código abierto escrito en C, lo que lo hace increíblemente rápido. Debido a sus operaciones de lectura/escritura más rápidas, se usa comúnmente para almacenar datos en caché. Los datos se almacenan en Redis en forma de valores-clave donde se utiliza una clave para extraer los valores.

Redis también puede conservar los datos en un disco, en lugar de mantenerlos en la memoria, utilizando "instantáneas", copiando su almacén de datos en la memoria a intervalos regulares.

Requisitos previos

Instalación de Redis {#instalación de Redis}

Redis se puede instalar fácilmente en Linux y macOS. Sin embargo, Windows requiere un poco de piratería. Instalaremos Redis en una instancia de AWS EC2 con Ubuntu 18.04 LTS:

1
$ sudo apt install redis-server

macOS, puede instalarlo a través de brew:

1
$ brew install redis

Después de instalar con éxito el paquete redis-server, verifiquemos el estado del proceso de Redis para verificar la instalación:

1
$ systemctl status redis

El resultado muestra el estado del redis-server, la dirección de enlace y el puerto en el que redis-server está escuchando:

1
2
3
4
5
6
7
8
9
 redis-server.service - Advanced key-value store
   Loaded: loaded (/lib/systemd/system/redis-server.service; enabled; vendor preset: enabled)`
   Active: active (running) since Tue 2020-03-10 10:06:30 UTC; 3min 2s ago
     Docs: http://redis.io/documentation,
           man:redis-server(1)
 Main PID: 2227 (redis-server)
    Tasks: 4 (limit: 1152)
   CGroup: /system.slice/redis-server.service
           └─2227 /usr/bin/redis-server 127.0.0.1:6379

De forma predeterminada, la configuración de Redis estará disponible en el archivo /etc/redis/redis.conf.

Habilitar la conexión remota a Redis

De forma predeterminada, solo se puede acceder a Redis desde localhost. Para habilitar la conexión remota a nuestro servidor Redis, actualice la dirección de enlace en la configuración de Redis a 0.0.0.0:

1
bind 0.0.0.0 ::1

Una vez actualizado, reinícialo:

1
$ systemctl restart redis

Configuración de Spring Boot

La forma más fácil de comenzar con una aplicación Spring Boot en blanco es usar Spring Initializr:

spring boot initializr

Alternativamente, también puede usar la CLI de arranque de primavera para arrancar la aplicación:

1
$ spring init --dependencies=spring-boot-starter-data-redis redis-spring-boot-demo

Comenzamos con la dependencia spring-boot-starter-data-redis, ya que incluye spring-data-redis, spring-boot-starter y lettuce-core.

Si ya tiene una aplicación Maven/Spring, agregue la dependencia a su archivo pom.xml:

1
2
3
4
5
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>${version}</version>
</dependency>

O si estás usando Gradle:

1
compile group: 'org.springframework.data', name: 'spring-data-redis', version: '${version}'

Conexión al servidor Redis

Como siempre, cuando trabajamos con servicios como Redis, queremos conectar nuestra aplicación al servicio. Hay varios conectores de Redis basados ​​en Java: jedis y Lechuga son dos opciones populares.

Conexión con Jedis

Para usar Jedis, tendremos que agregarlo a nuestro archivo pom.xml:

1
2
3
4
5
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>${version}</version>
</dependency>

O si estás usando Gradle:

1
compile group: 'redis.clients', name: 'jedis', version: '${version}'

Una vez que las dependencias están en su lugar, necesitamos configurar JedisConnectionFactory:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Configuration
public class Config {
    @Bean
    public JedisConnectionFactory redisConnectionFactory() {
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
        jedisConnectionFactory.setHostName("your_host_name_or_ip");
        jedisConnectionFactory.setPort(6379);
        jedisConnectionFactory.afterPropertiesSet();
        return jedisConnectionFactory;
    }
}

No hace falta decir que esta configuración se realiza en una clase @Configuration. Si desea leer más sobre Anotaciones principales de Spring Framework, ¡lo tenemos cubierto!

Conexión con lechuga

Lettuce es un conector Redis de código abierto y basado en Neto que se empaqueta con la dependencia inicial. La configuración de una fábrica de conexiones Lettuce es prácticamente la misma que para Jedis:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Configuration
public class Config {
    @Bean 
    public LettuceConnectionFactory redisConnectionFactory() {
        LettuceConnectionFactory lcf = new LettuceConnectionFactory();
        lcf.setHostName("your_host_name_or_ip");
        lcf.setPort(6379);     
        lcf.afterPropertiesSet();
        return lcf;
    }
}

Aunque su configuración es más o menos la misma, su uso es diferente. Lettuce permite operaciones asincrónicas y es seguro para subprocesos, a diferencia de Jedis, por ejemplo.

Plantilla Redis

RedisTemplate es una clase de entrada proporcionada por Spring Data a través de la cual interactuamos con el servidor Redis.

Pasaremos una instancia de RedisConnectionFactory a RedisTemplate para establecer una conexión:

1
2
3
4
5
6
public static RedisTemplate<String, User> redisTemplate() {
    RedisTemplate<String, User> redisTemplate = new RedisTemplate<String ,User>();
    redisTemplate.setConnectionFactory(redisConnectionFactory());
    redisTemplate.afterPropertiesSet();
    return redisTemplate;
}

Una vez establecida, RedisTemplate se convierte en la abstracción principal de las operaciones de Redis que podemos comandar. También se ocupa de la serialización y deserialización de objetos en matrices de bytes.

De forma predeterminada, RedisTemplate usa JdkSerializationRedisSerializer para serializar y deserializar objetos.

El mecanismo de serialización de RedisTemplate se puede cambiar, y Redis ofrece varios serializadores en el paquete org.springframework.data.redis.serializer.

StringRedisTemplate es una extensión de RedisTemplate centrada en pares clave-valor basados ​​en cadenas, por ejemplo.

Para admitir varias operaciones en diferentes tipos de datos, RedisTemplate proporciona clases de operaciones como ValueOperations, ListOperations, SetOperations, HashOperations, StreamOperations, etc.

Para operaciones relacionadas con hash, que usaremos para almacenar datos en nuestro servidor Redis, usaremos la clase HashOperations.

Operaciones hash

Redis Hashes puede contener un número n de pares clave-valor y está diseñado para usar menos memoria, lo que lo convierte en una excelente manera de almacenar objetos en la memoria. A través de la clase auxiliar HashOperations, podemos manipularlos.

Para usar cualquiera de estos, empaquetamos las operaciones hash devueltas desde la instancia RedisTemplate en la interfaz HashOperations:

1
HashOperations hashOperations = redisTemplate.opsForHash();

Estas operaciones incluyen operaciones básicas de mapas hash como put(), get(), entries(), etc.:

1
2
// Sets user object in USER hashmap at userId key
hashOperations.put("USER", user.getUserId(), user);
1
2
// Get value of USER hashmap at userId key
hashOperations.get("USER", userId);
1
2
// Get all hashes in USER hashmap
hashOperations.entries("USER");
1
2
// Delete the hashkey userId from USER hashmap
hashOperations.delete("USER", userId);

Definición de un repositorio de usuarios

Ahora sigamos adelante y creemos un repositorio de usuarios que realmente manejará las operaciones CRUD:

1
2
3
4
5
6
7
@Repository
public class UserRepository {
    private HashOperations hashOperations;
    public UserRepository(RedisTemplate redisTemplate) {
        this.hashOperations = redisTemplate.opsForHash();
    }
}

Por el contrario, en un repositorio típico, HashOperations sería algo así como SessionFactory. Con Redis, también puede crear un repositorio ampliando la interfaz CrudRepository y configurando una conexión Jedis en un @Bean.

En el constructor, pasamos nuestro redisTemplate, que debe configurarse con una fábrica de conexiones Redis.

Ahora, para poner una entrada, usaríamos:

1
hashOperations.put("USER", hashKey, value);

Una sola clave, como USER puede tener varios pares de hashKey:value. Se puede acceder a cada valor a través de hashKeys para una clave dada.

O para obtener una entrada, usaríamos:

1
value = hashOperations.get("USER", hashKey);

Definamos el Usuario:

1
2
3
4
5
6
public class User {
    private int userId;
    private String name;
    
    // Constructor, getters and setters
}

Con eso en mente, implementemos el resto del repositorio:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Repository
public class UserRepository {
    
    final Logger logger = LoggerFactory.getLogger(UserRepository.class);
    private HashOperations hashOperations;
    
    public UserRepository(RedisTemplate redisTemplate) {
        this.hashOperations = redisTemplate.opsForHash();
    }
    
    public void create(User user) {
        hashOperations.put("USER", user.getUserId(), user);
        logger.info(String.format("User with ID %s saved", user.getUserId()));
    }
    
    public User get(String userId) {
        return (User) hashOperations.get("USER", userId);
    }

    public Map<String, User> getAll(){
        return hashOperations.entries("USER");
    }
    
    public void update(User user) {
        hashOperations.put("USER", user.getUserId(), user);
        logger.info(String.format("User with ID %s updated", user.getUserId()));
    }
    
    public void delete(String userId) {
        hashOperations.delete("USER", userId);
        logger.info(String.format("User with ID %s deleted", userId));
    }
}

Ahora, para probar la aplicación, usemos nuestro userRepository:

1
2
3
4
5
6
UserRepository userRepository = new UserRepository(redisTemplate());

userRepository.create(new User("1", "username", "emailid"));
User user = userRepository.get("1");
userRepository.update(user);
userRepository.delete(user.getUserId());

Ejecutar este fragmento de código producirá:

1
2
3
2020-03-30 11:34:11.260  INFO 8772 --- [           main] c.h.redistutorial.UserRepository       : User with ID 1 saved
2020-03-30 11:34:11.260  INFO 8772 --- [           main] c.h.redistutorial.UserRepository       : User with ID 1 updated
2020-03-30 11:34:11.260  INFO 8772 --- [           main] c.h.redistutorial.UserRepository       : User with ID 1 deleted

Usemos un cliente Redis para ver los datos que se insertan, actualizan y eliminan.

  • Creando un usuario:
    created user in redis

  • Actualizar un usuario:
    get user from redis

  • Eliminar un usuario:
    user deleted from redis

Conclusión

Spring Boot es cada vez más preferido por los desarrolladores de Java/Spring porque es liviano y fácil de usar. Simplifica en gran medida el proceso de arranque de aplicaciones y lo ayuda a concentrarse en la lógica comercial real en lugar de interconectar las cosas.

Redis, por otro lado, es una base de datos en memoria extremadamente popular que la convierte en un gran compañero para los microservicios.

Redis se usa a menudo para la administración de caché por parte de microservicios para reducir la cantidad de llamadas de base de datos al servidor. En este nuevo mundo de sistemas de cobro de pago por uso, esto puede reducir efectivamente los costos operativos para negocios y empresas. s.