Per interagire con Redis da un'applicazione Node.js, esistono diverse alternative,
due delle quali vengono raccomandate dal sito ufficiale:
- node_redis: il più diffuso client
Redis per Node.js; - ioredis: client molto completo,
focalizzato sull'ottimizzazione delle prestazioni che contempla l'integrazione di caratteristiche avanzate
come Cluster, Sentinel e scripting Lua.
In questa lezione vedremo la prima opzione. Possiamo creare un nuovo progetto che ne preveda
l'utilizzo:
npm init
npm install redis --install
La libreria permette di gestire i database Redis in ogni aspetto, e ciò si noterà in tutti i casi in cui i metodi impiegati ricalcano i nomi delle funzioni
del DBMS disponibili da console testuale, mentre la lettura di dati avviene sempre in modalità asincrona.
Inizializzare il client
Il primo passo per l'uso di node_redis consiste nell'inizializzazione del client. Per fare
ciò, dovremo fornire una serie di parametri tra cui indirizzo di rete e porta TCP su cui
Redis è in ascolto, ed eventuale password di accesso fissata nel file di configurazione. Richiamando il metodo createClient
senza argomenti,
verranno utilizzati allo scopo tutti i valori di default:
var redis = require("redis")
var client = redis.createClient()
Nel seguente caso, invece, verranno forniti propri parametri incapsulati in un oggetto;
var redis = require("redis")
var client = redis.createClient(
{host:"192.168.56.3", "port":5555, "password":"parolasegreta"})
Una volta creato il client, si potranno gestire eventi ad esso collegati, tra cui ready (generato quando
la connessione è stabilita) ed error (che notifica situazioni anomale che impediscono il completamento della
connessione al server):
client.on("error", function (err) {
// codice per la gestione degli errori
});
client.on("ready", function (err) {
// attivazione delle azioni conseguenti alla connessione
});
Per chiudere una connessione, si può usare il metodo end
che termina il dialogo con il server senza
attendere la ricezione di ulteriori risposte, oppure quit
per una chiusura di connessione più graduale e completa.
Salvare e leggere stringhe
I metodi set
e get
permettono rispettivamente di scrivere e leggere stringhe in Redis:
// salviamo a=10
client.set("a","10");
// leggiamo "a"
client.get("a", function(err, data)
{
console.log(data)
})
Si noti che per utilizzare set
è stato sufficiente specificare chiave e valore, mentre get
si consuma in modalità asincrona: infatti, non otterremo il risultato direttamente come valore di ritorno,, bensì tramite la funzione di callback, nella quale troveremo accessibile sia un eventuale messaggio di errore,
sia il valore legato alla chiave.
Hashmap
Altro elemento estremamente utile in Redis sono le hashmap, ottime candidate alla memorizzazione di oggetti. Supponiamo di voler salvare
in Redis un oggetto Javascript siffatto:
p={
"name":"John",
"surname":"Smith",
"year":1982
}
Potremo salvare i dati in una hashmap sfruttando la funzione hmset
e recuperarli tutti con hgetall
:
client.hmset("person","name",p.name,"surname", p.surname, "year", p.year);
client.hgetall("person", function(err, data)
{
console.log(data.name+" "+data.surname+", "+data.year)
// stampa "John Smith, 1982"
})
La struttura dati è stata associata alla parola chiave "persona". In alternativa si sarebbe potuto recuperare solo alcuni elementi
inseriti nella hashmap specificando la chiave collegata:
client.hmget("person", "surname", function(err, data)
{
console.log(data)
// stampa [ 'Smith' ]
})
Le liste
Altra struttura dati disponibile in Redis è la lista. Ecco alcuni dei metodi disponibili per la loro gestione:
lpush
erpush
per l'inserimento in testa e in coda alla lista;llen
per conoscere la lunghezza della lista;lrange
per ottenere un segmento di oggetti contenuti nella struttura dati;lpop
erpop
per estrarre (restituendolo) l'elemento in testa e in coda nella lista.
Vediamo un esempio riassuntivo:
// inseriamo un pò di nomi in testa alla lista
client.lpush("nomi","Ivan", "Luigi", "Saverio", "Andrea");
// ora contiene "Andrea", "Saverio", "Luigi", "Ivan"
// inseriamo un pò di nomi in coda alla lista
client.rpush("nomi","Adamo", "Samuel", "Riccardo", "Nunzio");
// ora contiene "Andrea", "Saverio", "Luigi", "Ivan", "Adamo", "Samuel", "Riccardo", "Nunzio"
// stampa la lunghezza della lista
client.llen("nomi", function(err, data)
{
console.log("Lunghezza della lista: "+data);
});
// elenco dei primi sette nomi (indici da 0 a 6)
console.log("I primi sette nomi:");
client.lrange("nomi", 0, 6, function(err, data)
{
console.log(data);
})
// elimina quello in testa e lo restituisce
client.lpop("nomi", function(err, data)
{
console.log(data);
});
// elimina quello in coda e lo restituisce
client.rpop("nomi", function(err, data)
{
console.log(data);
});
L'output prodotto è il seguente:
I primi sette nomi:
Lunghezza della lista: 8
[ 'Andrea',
'Saverio',
'Luigi',
'Ivan',
'Adamo',
'Samuel',
'Riccardo',
'Nunzio' ]
Andrea
Nunzio
Uso delle promises
La libreria node_redis permette anche l'utilizzo delle promises.
Per sfruttare questo approccio, dobbiamo creare delle versioni promisified di alcune funzioni.
const {promisify} = require('util');
const getProm = promisify(client.get).bind(client);
Così facendo definiamo la funzione getProm
con cui possiamo invocare, secondo lo stile delle promises, il metodo get
del client:
getProm("a").then(function(data)
{
console.log(data)
})
Utilizzare il modello Pub/Sub
Redis può essere usato anche per applicare il modello Pub/Sub, secondo il quale uno o più componenti si incaricano di essere "publisher" di contenuti
indicando il canale su cui pubblicarli. Altri compenenti potranno invece svolgere il ruolo di subscriber, iscrivendosi al canale e ricevendo notifica ogni volta
che verranno pubblicati nuovi contenuti su di esso. Il meccanismo è utile per realizzare dei sistemi di scambio di messaggi
che possono essere impiegati nel coordinamento di più applicazioni, distribuzione di contenuti e raccolta dati da sistemi diversi.
Vediamo i passaggi per implementare soluzioni di questo tipo.
Per prima cosa, definiamo i vari publisher/subscriber, ognuno dei quali sarà un client Redis:
subscriber = redis.createClient({host:"192.168.56.3", "port":5555, "password":"secret"}),
publisher = redis.createClient({host:"192.168.56.3", "port":5555, "password":"secret"});
Poi, definiamo un publisher che istituirà il canale "notizie" sul quale pubblicherà dei messaggi:
publisher.publish("notizie", "Sventata rapina a Roma");
publisher.publish("notizie", "Incidente sulla A1");
Infine, un subscriber estrarrà singoli messaggi e li manderà in output, il tutto in maniera sintetica ma molto chiara: infatti, esso
riceve due elementi nella funzione di callback che svolgono, rispettivamente, il ruolo di "channel" e "messaggio", ossia tutto ciò di indispensabile
a ricevere la comunicazione:
// come si gestisce ogni messaggio ricevuto
subscriber.on("message", function(channel, message) {
console.log("Notizia (sul canale '" + channel + "'): "+message)
});
subscriber.subscribe("notizie");
Questo l'output che si ottiene:
Notizia (sul canale 'notizie'): Sventata rapina a Roma
Notizia (sul canale 'notizie'): Incidente sulla A1