Node.js + redis. Implementando un “what’s hot” con tiempo de expiración

node_redis

 

En este tutorial vamos a ver como implementar un servicio “what’s hot” utilizando un servidor node.js y una base de datos no-relacional de tipo clave-valor como es redis. ¿Qué hace este servicio? Simplemente nos devuelve las camisetas más vistas durante el último día.

Partimos de una API RESTful que implementa los casos CRUD para una tienda de camisetas y que ya vimos en entradas anteriores. Lo que haremos será lo siguiente:

  • Accedemos a ver una camiseta GET /tshirt/:id
  • Al acceder, guardaremos en una BD redis una clave cuyo valor será el número de accesos que ha tenido la camiseta. La clave tendrá la siguiente forma: hot.id => 4. Además, la clave tendrá un tiempo de expiración de 86400 segundos, es decir, de un día.
  • Si queremos conocer las camisetas más vistas en el último día, accederemos a /hots con el método GET, y obtendremos las “camisetas calientes”.

Si queréis podéis consultar en SpeakinBytes información sobre redis.

Para empezar, podéis descargar el código de la aplicación base desde github. Además, debéis tener instalado y corriendo un servidor MongoDB así como uno redis. Tenéis también que poner a correr el servidor node.js y haber creado algunas camisetas.

Lo primero que haremos será instalar redis en nuestro proyecto. Para ello, en nuestro terminal y dentro de la carpeta de nuestro proyecto (si lo habéis descargado desde github, será node-api-rest), ejecutamos el comando npm install redis.

A continuación vamos a nuestro fichero thirts.js, que está dentro de la carpeta routes.

Lo primero será añadir al principio la conexión a redis.

Ahora vamos a nuestra función findById. Recordemos que se accede a ella al acceder a /tshirt/:id y que lo que hace es mostrar una camiseta. En dicha función queremos utilizar redis para guardar una pareja clave-valor con un tiempo de expiración de la clave de 86400 segundos (1 día). Por tanto, lo que haremos será:

  • 1.- Si la clave no existe todavía, la creamos con valor 1 y le ponemos el tiempo de expiración
  • 2.- Si la clave existe, simplemente incrementamos el valor en 1 y reiniciamos el tiempo de expiración
  • Nota1.- El formato de la clave será: hot.idcamiseta
  • Nota2.- Crearemos la clave o incrementaremos su valor siempre y cuando hayamos verificado primero que existe la camiseta. Este manejo de errores ya estaba implementado anteriormente.

Veamos primero el código sin incluir el tiempo de expiración:

Teniendo nuestro servidor de MongoDB arrancado (y previo a haber añadido algunas camisetas) y nuestro servidor redis, arrancamos el servidor node con el comando node server.js. A continuación utilizamos postman para acceder a /tshirt/:id. Tras esto, si vamos a nuestro cliente redis, que podemos arrancar en una nueva ventana del terminal con el comando redis-cli, comprobaremos que se ha creado la pareja clave-valor. Si volvemos a acceder a la misma ruta, veremos que el valor se ha incrementado en 1.

Ahora vamos a añadir el tiempo de expiración. Lo primero será añadir una variable (tipo macro) al principio del fichero tshirts.js. Yo la llamaré ttl  y le asignaré el valor 86400.

En el caso de que estemos creando la clave con el set, ahora utilizaremos setex, para indicarle el tiempo de expiración. En caso de que estemos incrementando el valor, después haremos un expire. Lo vemos mejor en el código.

Las dos instrucciones que están seguidas, el incr y el expire, podríamos haberlas agrupado en un multi-exec.

Muy bien, vamos a probar lo que tenemos hasta ahora. Recordar tener arrancado MongoDB, redis y el servidor node. Por un lado utilizamos una vez más postman para acceder a /thsirt/:id. Vemos en nuestro terminal donde tenemos arrancado el servidor node el mensaje:

Si volvemos a utilizar postman veremos lo siguiente en la consola de nuestro servidor node:

Y en nuestro cliente redis (arrancado con el comando redis-cli) vemos:

Perfecto! Con esto ya guardamos un conjunto de clave-valor con tiempo de expiración en nuestro servidor redis cada vez que accedemos a ver una camiseta. Ahora vamos a configurar el servicio para que nos devuelva las camisetas que tenemos almacenadas en redis, es decir, las “camisetas calientes”.

Lo primero es añadir a nuestras rutas el /hots, y a continuación crear la función hot. Todo esto en nuestro fichero /routes/tshirts.js

Ahora debemos programar esta función. A grosso modo, lo que debemos hacer es:

  • Encontrar todas las claves que empiecen por “hot.”, es decir, nuestras “camisetas calientes”
    • client.keys(“hot.*”, function (err, replies){…}
  • Recorrer el array de claves que tenemos
    • replies.forEach(function(item){…}
  • A medida que vamos recorriendo, buscar la camiseta correspondiente en función de su id que tenemos almacenada en redis y guardarla en un vector. Aquí utilizaremos la función substring, debido a que estamos buscando una camiseta por su id, y a que nosotros habíamos guardador “hot.id”, de forma que con dicha función tendremos la string de nuestra id sin el hot. delante
    • Tshirt.findById(item.substring(4), function (err, tshirt){…}
  • Mostrar el vector de las “camisetas calientes”
    • res.send(hotTshirts);

El código que os muestro a continuación es totalmente razonable, sin embargo, no es correcto. Es más, sino os digo que algo está mal, probablemente no os hubierais dado cuenta hasta ejecutarlo. Primero os lo enseño y luego os explico dónde está el error.

Realmente el código sigue los pasos que dijimos anteriormente, sin embargo, no tiene en cuenta algo muy importante:

En node.js, las llamadas a la base de datos son asíncronas, por lo que no se queda esperando un resultado, sino que continúa con el resto de operaciones.

Si ejecutamos el código, vemos que primero se muestra “Este mensaje se muestra el primero”, a continuación un array vacío [] y por último las camisetas almacenadas como “calientes” y el mensaje “hotShirts add”. Esto es debido a que en node.js, las llamadas a la base de datos se hacen de forma asíncrona.

Os voy a poner el código que resuelve este problema. Si queréis ver más información, podéis acceder a nuestro post sobre llamadas asíncronas en Node.js. Antes de ejecutarlo debéis hacer dos cosas. La primera, configurar las dependencias de async es vuestro fichero Package.json y lo segundo instalarlas.

Instalando…

Código de la función hot, en /routes/tshirts.js

Y con esto tenemos lista nuestra funcionalidad “what’s hot” con un servidor Node.js y una BD no relacional de tipo clave-valor como es redis.

Podemos probar que todo funciona correctamente. Para ello, utilizamos postman y accedemos a algunas camisetas. A continuación accedemos con un GET a /hots y vemos los resultados:

whatshot

Podéis descargar los fuentes desde GitHub.


One Comment

So, what do you think ?