En este documento vamos a ver cómo mejorar el rendimiento de nuestras aplicaciones web mediante la realización de peticiones a una memoria caché del servidor o caching.
En aplicaciones web, las peticiones a caché pueden referirse a diferentes tipos de caché. Dos de los más comunes son:
Caché del navegador: los navegadores web tienen por defecto una caché integrada para mejorar el rendimiento al acceder a sitios web. Se trata de una memoria temporal en el navegador del usuario que almacena recursos estáticos como HTML, CSS, JavaScript e imágenes. Esto permite que las páginas web se carguen más rápidamente en visitas subsecuentes sin tener que descargar todos los recursos nuevamente del servidor.
Caché del servidor: es una memoria rápida utilizada en el servidor para almacenar datos que se consultan frecuentemente desde una base de datos. Este proceso reduce la carga en la base de datos principal y mejora el tiempo de respuesta de la aplicación.
En este apartado nos vamos a centrar en las peticiones a caché del servidor. Este tipo de caché juega un papel importante en el rendimiento de las aplicaciones web modernas. A diferencia de la caché del navegador, que es gestionada automáticamente por el navegador y se limita a recursos estáticos, la caché del servidor puede ser configurada y optimizada de manera precisa para almacenar datos dinámicos y consultas de base de datos, proporcionando beneficios significativos en entornos de alta demanda y aplicaciones complejas.
Las peticiones a caché del servidor funcionan almacenando temporalmente los datos que se consultan con frecuencia en una memoria rápida o caché, lo que permite acceder a esos datos más rápidamente en futuras solicitudes.
A continuación, se explica el funcionamiento de estas peticiones paso a paso:
Primera petición del cliente:
Peticiones siguientes:
Ventajas
Inconvenientes
Redis (REmote DIctionary Server) es un almacén de estructura de datos en memoria, de código abierto, que se utiliza como caché de aplicaciones y/o base de datos NoSQL de respuesta rápida.
Redis sigue una arquitectura cliente-servidor, lo que significa que hay dos componentes principales en su funcionamiento: el servidor Redis y los clientes Redis.
Utiliza un modelo de datos clave-valor, donde las claves son identificadores únicos, cuyo valor puede ser uno de los muchos tipos de datos que admite Redis (cadenas, listas, conjuntos, hashes y más).
Una de sus principales características es que puede ser configurado para trabajar en un entorno distribuido utilizando mecanismos como la replicación y los clústeres de Redis. Esto permite que los datos se repliquen y se distribuyan entre varios nodos, asegurando su disponibilidad y durabilidad. Además, Redis ofrece soporte para diferentes lenguajes de programación, incluyendo Java, Python, PHP, C, C++, C#, Javascript, entre otros.
Otras plataformas similares a Redis
Para instalar Redis en una distribución basada en Debian como Ubuntu y asegurar que esté funcionando y se inicie automáticamente con el sistema, sigue los siguientes pasos:
sudo apt update
sudo apt install redis-server
sudo systemctl start redis-server
redis-cli ping
. Si Redis está funcionando, deberías ver la respuesta PONG.sudo systemctl status redis-server
. Este comando muestra información sobre el estado actual del servicio.Opcionalmente, se puede configurar Redis para que se inicie automáticamente al arrancar el sistema con el siguiente comando: sudo systemctl enable redis-server
.
Si surge algún problema al iniciar el servicio, revisa los logs de Redis. Suelen encontrarse generalmente en /var/log/redis/redis-server.log
.
El archivo de configuración de Redis generalmente se llama redis.conf
y encuentra en /etc/redis/
. Es posible editarlo para incluir medidas de seguridad, como establecer una contraseña o limitar las conexiones a direcciones IP confiables. Trataremos esto en un apartado posterior.
Redis no es oficialmente compatible con Windows. Para instalar Redis en Windows, primero hay que instalar WSL2 (Windows Subsystem for Linux). WSL2 permite ejecutar binarios de Linux de forma nativa en Windows. Para que este método de instalación funcione, es necesario disponer de Windows 10 versión 2004 o superior, o Windows 11. Para versiones anteriores, consulta la página de instalación manual.
Paso 1. Instalar WSL2
El proceso de instalación también lo puedes consultar en las instrucciones detalladas para instalar WSL proporcionadas por Microsoft.
wsl --install
. Este comando habilitará las características necesarias para ejecutar WSL e instalará de forma predeterminada Ubuntu. Esta distribución predeterminada puede ser cambiada posteriormente o durante la instalación usando parámetros adicionales.sudo
.exit
(esto te sacará de la sesión actual de WSL y te devolverá a PowerShell o a la línea de comandos de Windows) y reinicia tu máquina.Paso 2. Iniciar sesión en WSL
wsl
: esto iniciará tu distribución de Linux predeterminada en WSL.wsl -d Ubuntu
: esto iniciará una distribución concreta en WSL, en este caso, la de Ubuntu.lsb_release -a
Paso 3. Actualizar y mejorar la distribución
Windows no actualiza ni mejora automáticamente las distribuciones de Linux que tengas instaladas. Es una buena práctica mantener los paquetes de la distribución de Linux actualizados para asegurar un buen rendimiento y seguridad.
Para ello, abre tu terminal de WSL y ejecuta los siguientes comandos: sudo apt update
y sudo apt upgrade
.
Paso 4. Instalación de Redis
Sigue los siguientes pasos para instalar versiones estables recientes de Redis desde el repositorio oficial de APT desde tu terminal WSL. El proceso de instalación también lo puedes consultar en la documentación oficial de Redis.
curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor \
-o /usr/share/keyrings/redis-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] \
https://packages.redis.io/deb $(lsb_release -cs) main" | \
sudo tee /etc/apt/sources.list.d/redis.list
Actualizar los índices de los paquetes:
sudo apt-get update
Instalar Redis:
sudo apt-get install redis
Siguiendo estos pasos, deberías tener Redis instalado y funcionando en tu sistema Windows mediante WSL.
Paso 5. Iniciar y verificar el servicio
sudo service redis-server start
sudo service redis-server status
. Este comando debería mostrar información sobre el estado actual del servicio Redis. Si el servicio está activo y funcionando correctamente, se obtendrá una salida similar a la siguiente:Otra forma de comprobar si Redis está activo es utilizando el cliente de Redis para enviar un comando PING y recibir una respuesta PONG con el siguiente comando: redis-cli ping
Opcionalmente, se puede configurar Redis para que se inicie automáticamente al arrancar el sistema con el siguiente comando: sudo systemctl enable redis-server
.
Si surge algún problema al iniciar el servicio, es conveniente revisar los logs de Redis, que suelen encontrarse en /var/log/redis/redis-server.log
ó reiniciar el servicio con sudo service redis-server restart
.
El archivo de configuración de Redis generalmente se llama redis.conf
y encuentra en /etc/redis/
. Es posible editarlo para incluir medidas de seguridad, como establecer una contraseña o limitar las conexiones a direcciones IP confiables. Trataremos esto en el apartado siguiente.
A continuación, se detallan algunas operaciones adicionales que pueden ser útiles para la administración de tu distribución de Linux en WSL.
Listar las distribuciones instaladas
Para ver todas las distribuciones de Linux instaladas y su estado, utiliza: wsl -l -v
Si tienes instalado Docker Desktop, es posible que esté configurado para que se inicie automáticamente y utilice WSL2 como backend. Esto puede provocar que se inicie Docker Desktop en lugar de Ubuntu. Si esto ocurre, puedes iniciar una distribución concreta con el comando wsl -d <NombreDistro>
, que hemos visto antes, o bien modificar la distribución predeterminada, como se explica a continuación.
Establecer una distribución como predeterminada
Si quieres que una determinada distribución sea la predeterminada para que se inicie cada vez que se ejecute wsl
, puedes establecerla así: wsl --set-default <NomDistro>
.
En este caso, si queremos establecer que Ubuntu sea la distribución predeterminada, ejecutaremos desde PowerShell el siguiente comando: wsl --set-default Ubuntu
Cambiar contraseña
Si quieres cambiar la contraseña de tu distribución predeterminada, sigue estos pasos:
passwd
.Restablecer la contraseña
Si has olvidado la contraseña de tu distribución predeterminada, sigue estos pasos:
wsl -u root
passwd <nombredeusuario>
, donde Una vez instalado, se puede configurar Redis mediante el archivo de configuración redis.conf
, que generalmente se encuentra en /etc/redis/
. Para editar el archivo de configuración, escribe el siguiente comando: sudo nano /etc/redis/redis.conf
en tu terminal WSL.
Una configuración básica, incluye las siguientes medidas de seguridad:
Configurar una contraseña.
Se utiliza la directiva requirepass
seguida de una contraseña fuerte. Esta directiva asegura que cualquier cliente que intente conectarse a Redis debe proporcionar la contraseña correcta para poder ejecutar comandos, lo que protege la instancia de Redis de accesos no autorizados.
Limitar las conexiones a direcciones IP confiables.
Es posible configurar Redis para que solo acepte conexiones de direcciones IP específicas, normalmente localhost. Para ello, se emplea la directiva bind
seguida de la dirección IP de localhost, 127.0.0.1 para IPv4 y -::1 para IPv6. De esta forma solo los procesos que se ejecuten en la misma máquina pueden conectarse a Redis, evitando conexiones remotas.
Habilitar el modo protegido.
El modo protegido es una capa adicional de seguridad que entra en juego cuando Redis está configurado de manera insegura (como escuchar en todas las interfaces sin una contraseña). No es necesario habilitar este modo cuando se utilizan requirepass
y bind 127.0.0.1
, pero es recomendable mantenerlo habilitado como una medida de seguridad adicional. Para habilitar este modo, se utiliza la directiva protected-mode
seguida de la palabras yes
Según esto, la configuración recomendada de redis.conf sería la siguiente:
requirepass tucontraseña
bind 127.0.0.1 -::1
protected-mode yes
Otros mecanismos de seguridad incluyen el uso de TLS (Transport Layer Security) para cifrar el tráfico entre el cliente y el servidor, o el uso de un firewall para limitar el acceso al puerto de Redis solo a las IPs o redes que realmente necesitan acceso, pero esto queda fuera del alcance de este curso.
Para conectarnos a Redis desde una aplicación Node.js, necesitamos una biblioteca cliente de Redis específica para este lenguaje. Los clientes de Redis cumplen con las siguientes funciones:
En el caso de Node.js, existen dos clientes populares para Redis: ioredis
y redis
. Ambos clientes ofrecen APIs de programación similares, envolviendo cada comando de Redis en una función que podemos llamar desde Node.js.
Utilizaremos ioredis
, ya que ofrece soporte nativo para Promises, lo que facilita la escritura de código asincrónico sin necesidad de bibliotecas adicionales. Los métodos de ioredis
retornan Promises por defecto, lo que permite el uso de async/await
de manera sencilla.
Instalación
Redis es tan fácil de instalar como cualquier otro módulo que queramos incorporar a un proyecto Node. Simplemente necesitamos ejecutar el correspondiente comando npm install
en la carpeta del proyecto donde queramos añadirlo (y, previamente, el comando npm init
en el caso de que aún no hayamos creado el archivo package.json):
npm install ioredis
Ejemplo básico de uso
Vamos a crear un proyecto llamado “PruebaCaching” en la carpeta de “ProyectosNode/Pruebas”, en el que ejecutaremos comandos básicos SET y GET para almacenar y recuperar valores en la base de datos Redis. Lo primero que tenemos que hacer es instalar Redis, y después crear un archivo index.js
con este código:
const redis = require('redis');
const client = redis.createClient();
client.set('key', 'value', redis.print);
client.get('key', (err, reply) => {
console.log(reply); // 'value'
});
El código, como vemos, es muy sencillo. Primero incluimos la librería y creamos una nueva instancia de un cliente de Redis utilizando el método createClient
de la biblioteca Redis. Esto establece una conexión con el servidor de Redis, permitiendo interactuar con la base de datos.
A continuación ejecutamos el comando set
de Redis para almacenar el par clave-valor (mi-nombre
y may
) en la base de datos Redis. Como tercer parámetro, utilizamos una función de callback, propocionada por la biblitoeca Redis, para imprimir el resultado de la operación, redis.print
. Esto imprimirá Reply: OK si la operación se realiza con éxito.
Finalmente, ejecutamos el comando get
de Redis para recuperar el valor el valor asociado con mi-nombre
. Le pasaremos como segundo argumento una función callback para manejar el resultado. Si ocurre un error durante la operación, err
contendrá el error (en este caso, no se está manejando explícitamente), y reply
contendrá el valor asociado con mi-nombre
si la operación tiene éxito. La consola imprimirá may.
Redis proporciona diversas operaciones básicas, entre ellas:
client.set('key', 'value', redis.print);
client.get('key', (err, reply) => {
console.log(reply); // 'value'
});
client.del('key', (err, reply) => {
console.log(reply); // 1 if deleted
});
client.set('counter', 100);
client.incr('counter', (err, reply) => {
console.log(reply); // 101
});
client.expire('key', 60); // Expira en 60 segundos
const express = require('express');
const app = express();
const redis = require('redis');
const client = redis.createClient();
// Middleware para cachear respuestas
function cache(req, res, next) {
const { id } = req.params;
client.get(id, (err, data) => {
if (err) throw err;
if (data !== null) {
res.send(JSON.parse(data));
} else {
next();
}
});
}
app.get('/data/:id', cache, (req, res) => {
const { id } = req.params;
// Supongamos que fetchData es una función que obtiene datos de la base de datos
fetchData(id).then((data) => {
client.setex(id, 3600, JSON.stringify(data)); // Cachea la respuesta por 1 hora
res.send(data);
});
});
function fetchData(id) {
// Simulación de una consulta a base de datos
return new Promise((resolve) => {
setTimeout(() => {
resolve({ id, data: "Data from database" });
}, 200);
});
}
app.listen(3000, () => {
console.log('Server running on port 3000');
});
Para gestionar adecuadamente los datos en la caché y evitar que se queden obsoletos, podemos utilizar varias estrategias y buenas prácticas. Aquí hay algunas recomendaciones clave:
client.setex('key', 3600, 'value'); // Expira en 1 hora
function updateData(id, newData) {
// Actualizar datos en la base de datos
database.update(id, newData).then(() => {
// Eliminar la entrada de la caché
client.del(id);
});
}
function getData(id) {
return new Promise((resolve, reject) => {
client.get(id, (err, data) => {
if (err) reject(err);
if (data !== null) {
resolve(JSON.parse(data));
} else {
// Datos no están en caché, recuperarlos de la base de datos
database.get(id).then((result) => {
// Almacenar los datos en la caché y resolver la promesa
client.setex(id, 3600, JSON.stringify(result)); // TTL de 1 hora
resolve(result);
}).catch(reject);
}
});
});
}
function updateData(id, newData) {
// Actualizar la caché y la base de datos
client.set(id, JSON.stringify(newData), (err) => {
if (err) throw err;
database.update(id, newData);
});
}
function getData(id) {
client.get(id, (err, data) => {
if (err) throw err;
if (data) {
return JSON.parse(data);
} else {
database.get(id).then((result) => {
client.set(id, JSON.stringify(result));
return result;
});
}
});
}