Redis
 sql >> Base de données >  >> NoSQL >> Redis

Utilisez redis pour créer un chat en temps réel avec socket.io et NodeJs

Redis est plus qu'un magasin clé-valeur.

Donc, vous voulez ce qui suit :

  • messages de chat,
  • discussions à deux,
  • vous n'avez pas mentionné les contraintes de temps, supposons donc que vous archivez les messages après un certain temps,
  • vous ne dites pas non plus si vous voulez des "fils" séparés entre deux personnes, comme des forums ou des messages continus, comme facebook. Je suppose continu.

Pour chaque utilisateur, vous devez stocker les messages qu'il envoie. Disons APP_NAMESPACE:MESSAGES:<USER_ID>:<MESSAGE_ID> . Nous ajoutons ici userId afin de pouvoir récupérer facilement tous les messages envoyés par un seul utilisateur.

Et, pour chaque deux utilisateurs, vous devez suivre leurs conversations. En tant que clé, vous pouvez simplement utiliser leurs ID utilisateur APP_NAMESPACE:CONVERSATIONS:<USER1_ID>-<USER2_ID> . Pour vous assurer que vous obtenez toujours la même conversation partagée pour les deux utilisateurs, vous pouvez trier leurs identifiants par ordre alphabétique, de sorte que les utilisateurs 132 et 145 auront tous deux 132:145 comme clé de conversation

Alors que stocker dans les "conversations" ? Utilisons une liste :[messageKey, messageKey, messageKey] .

Ok, mais quel est maintenant le messageKey ? Combinaison de userId ci-dessus et d'un messageId (afin que nous puissions obtenir le message réel).

Donc, fondamentalement, vous avez besoin de deux choses :

  1. Stockez le message et attribuez-lui un identifiant
  2. Stocker une référence à ce message dans la conversation concernée.

Avec le nœud et le client redis/hiredis standard, cela ressemblerait à quelque chose comme (je vais ignorer les vérifications d'erreur évidentes, etc., et j'écrirai ES6. Si vous ne pouvez pas encore lire ES6, collez-le simplement dans babel) :

 // assuming the init connects to redis and exports a redisClient
import redisClient from './redis-init';
import uuid from `node-uuid`;


export function storeMessage(userId, toUserId, message) {

  return new Promise(function(resolve, reject) {

    // give it an id.
    let messageId = uuid.v4(); // gets us a random uid.
    let messageKey = `${userId}:${messageId}`;
    let key = `MY_APP:MESSAGES:${messageKey}`;
    client.hmset(key, [
      "message", message,
      "timestamp", new Date(),
      "toUserId", toUserId
    ], function(err) {
      if (err) { return reject(err); }

      // Now we stored the message. But we also want to store a reference to the messageKey
      let convoKey = `MY_APP:CONVERSATIONS:${userId}-${toUserId}`; 
      client.lpush(convoKey, messageKey, function(err) {
        if (err) { return reject(err); }
        return resolve();
      });
    });
  });
}

// We also need to retreive the messages for the users.

export function getConversation(userId, otherUserId, page = 1, limit = 10) {
  return new Promise(function(resolve, reject) {
    let [userId1, userId2] = [userId, otherUserId].sort();
    let convoKey = `MY_APP:CONVERSATIONS:${userId1}-${userId2}`;
    // lets sort out paging stuff. 
    let start = (page - 1) * limit; // we're zero-based here.
    let stop = page * limit - 1;
    client.lrange(convoKey, start, stop, function(err, messageKeys) {

      if (err) { return reject(err); }
      // we have message keys, now get all messages.
      let keys = messageKeys.map(key => `MY_APP:MESSAGES:${key}`);
      let promises = keys.map(key => getMessage(key));
      Promise.all(promises)
      .then(function(messages) {
         // now we have them. We can sort them too
         return resolve(messages.sort((m1, m2) => m1.timestamp - m2.timestamp));
      })
      .catch(reject);
    }); 
  });
}

// we also need the getMessage here as a promise. We could also have used some Promisify implementation but hey.
export function getMessage(key) {
  return new Promise(function(resolve, reject)  {
    client.hgetall(key, function(err, message) {
      if (err) { return reject(err); }
      resolve(message);
    });
  });
}

C'est grossier et non testé, mais c'est l'essentiel de la façon dont vous pouvez le faire.