¿Cómo usar redis PUBLISH / SUBSCRIBE con nodejs para notificar a los clientes cuando cambian los valores de los datos?

99

Estoy escribiendo una aplicación de publicación / suscripción basada en eventos con NodeJS y Redis. Necesito un ejemplo de cómo notificar a los clientes web cuando cambian los valores de los datos en Redis.

guilin 桂林
fuente

Respuestas:

123

VIEJO solo usa una referencia

Dependencias

usa express , socket.io , node_redis y, por último, pero no menos importante, el código de muestra de media fire.

Instalar node.js + npm (como no root)

Primero debe (si aún no lo ha hecho) instalar node.js + npm en 30 segundos (de la manera correcta porque NO debe ejecutar npm como root ):

echo 'export PATH=$HOME/local/bin:$PATH' >> ~/.bashrc
. ~/.bashrc
mkdir ~/local
mkdir ~/node-latest-install
cd ~/node-latest-install
curl http://nodejs.org/dist/node-latest.tar.gz | tar xz --strip-components=1
./configure --prefix=~/local
make install # ok, fine, this step probably takes more than 30 seconds...
curl http://npmjs.org/install.sh | sh

Instalar dependencias

Después de instalar node + npm, debe instalar las dependencias emitiendo:

npm install express
npm install socket.io
npm install hiredis redis # hiredis to use c binding for redis => FAST :)

Descargar muestra

Puede descargar una muestra completa de mediafire .

Descomprimir paquete

unzip pbsb.zip # can also do via graphical interface if you prefer.

¿Qué hay dentro de la cremallera?

./app.js

const PORT = 3000;
const HOST = 'localhost';

var express = require('express');

var app = module.exports = express.createServer();

app.use(express.staticProvider(__dirname + '/public'));

const redis = require('redis');
const client = redis.createClient();

const io = require('socket.io');

if (!module.parent) {
    app.listen(PORT, HOST);
    console.log("Express server listening on port %d", app.address().port)

    const socket  = io.listen(app);

    socket.on('connection', function(client) {
        const subscribe = redis.createClient();
        subscribe.subscribe('pubsub'); //    listen to messages from channel pubsub

        subscribe.on("message", function(channel, message) {
            client.send(message);
        });

        client.on('message', function(msg) {
        });

        client.on('disconnect', function() {
            subscribe.quit();
        });
    });
}

./public/index.html

<html>
<head>
    <title>PubSub</title>
    <script src="/socket.io/socket.io.js"></script>
    <script src="/javascripts/jquery-1.4.3.min.js"></script>
</head>
<body>
    <div id="content"></div>
    <script>    
        $(document).ready(function() {
            var socket = new io.Socket('localhost', {port: 3000, rememberTransport: false/*, transports: ['xhr-polling']*/});
            var content = $('#content');

            socket.on('connect', function() {
            });

            socket.on('message', function(message){
                content.prepend(message + '<br />');
            }) ;

            socket.on('disconnect', function() {
                console.log('disconnected');
                content.html("<b>Disconnected!</b>");
            });

            socket.connect();
        });
    </script>
</body>
</html>

Iniciar servidor

cd pbsb    
node app.js

Iniciar navegador

Lo mejor es iniciar Google Chrome (debido a la compatibilidad con websockets, pero no es necesario). Visita http://localhost:3000para ver una muestra (al principio no ves nada más PubSubque un título).

Pero en el publishcanal pubsub, debería ver un mensaje. A continuación publicamos "Hello world!"en el navegador.

De ./redis-cli

publish pubsub "Hello world!"
Alfredo
fuente
¿Por qué necesitas const client = redis.createClient()en la raíz de app.js?
Akasha
no es necesario utilizar const en absoluto. var también podría usarse y tal vez debería haberlo hecho porque const solo está disponible en los motores javascript más nuevos. Además, esta línea asegura que estamos conectados al servidor Redis que usamos en este ejemplo.
Alfred
5
La muestra es muy antigua, por lo que no está actualizada con los últimos módulos socket.io/express y tal vez incluso con node.js. Intentaría actualizar el código. También hay otro gran problema con este código que abre otra conexión redis para cada usuario conectado. Eso solo debería estar activado en su lugar. Primero tengo que trabajar, pero después intento actualizar el código.
Alfred
1
Muy bien. Sigo pensando que hay margen para algunas mejoras que, cuando tenga tiempo, pondré online. Pero ahora estoy realmente trabajando duro: $.
Alfred
1
Creo que subscribe.on debe estar fuera del bloque socket.on ('conexión') para evitar múltiples suscripciones /
zubinmehta
26

aquí hay un ejemplo simplificado sin tantas dependencias. Todavía necesitasnpm install hiredis redis

El nodo JavaScript:

var redis = require("redis"),
    client = redis.createClient();

client.subscribe("pubsub");
client.on("message", function(channel, message){
  console.log(channel + ": " + message);
});

... poner eso en un archivo pubsub.js y ejecutar node pubsub.js

en redis-cli:

redis> publish pubsub "Hello Wonky!"
(integer) 1

que debería mostrarse: pubsub: Hello Wonky!en el nodo en ejecución del terminal! ¡Felicidades!

Adicional 23/4/2013: También quiero señalar que cuando un cliente se suscribe a un canal pub / sub, entra en modo de suscriptor y está limitado a los comandos de suscriptor. Solo necesitará crear instancias adicionales de clientes de redis. client1 = redis.createClient(), client2 = redis.createClient()por lo que uno puede estar en modo de suscriptor y el otro puede emitir comandos DB regulares.

nak
fuente
Aquí, cuando agregamos datos a los redis, ¿debería ejecutar publicar pubsub para recibir la notificación de inserción?
IshaS
1
@IshaS si eso es lo que necesitas hacer, sí. También debe examinar las transacciones si necesita ejecutar varios comandos de forma atómica: redis.io/commands/exec
nak
@nak Esto funcionó a la perfección en un GO :) Algunos usuarios pueden necesitar instalar 'cola de dos extremos' si aún no está instalado.
Alex
1
También vale la pena mencionar que si desea usar comodines, por ejemplo, suscríbase para pubsub/*agregar pal ejemplo: reemplazar subscibecon psubscribey messagecon pmessage.
Liosha Bakoushin
7

Ejemplo completo de Redis Pub / Sub ( chat en tiempo real con Hapi.js y Socket.io)

Estábamos tratando de entender Redis Publish / Subscribe (" Pub / Sub ") y todos los ejemplos existentes estaban desactualizados, eran demasiado simples o no tenían pruebas. ¡Así que escribimos un chat en tiempo real completo usando Hapi.js + Socket.io + Redis Pub / Sub Example con pruebas de extremo a extremo !

https://github.com/dwyl/hapi-socketio- redis-chat-example

El componente Pub / Sub tiene solo unas pocas líneas de código node.js: https://github.com/dwyl/hapi-socketio-redis-chat-example/blob/master/lib/chat.js#L33-L40

En lugar de pegarlo aquí ( sin ningún contexto ), le recomendamos que consulte / pruebe el ejemplo .

Lo construimos usando Hapi.js pero el chat.jsarchivo está desacoplado de Hapi y se puede usar fácilmente con un servidor http básico de node.js o express (etc.)

nelsonico
fuente
¿tienes este ejemplo con express?
Gixty
@Gixty escribimos el ejemplo usando Hapi.js porque todos los demás ejemplos usan Express.js ... como se menciona en la publicación, es trivial portarlo a cualquier otro marco Node.js (simplemente pase la aplicación express / escucha el código de inicio de chat.js) y funciona exactamente igual. PD: si eres nuevo en Hapi.js, consulta: github.com/nelsonic/learn-hapi
nelsonic
4

Maneje los errores de redis para evitar que nodejs salga. Puede hacerlo escribiendo;

subcribe.on("error", function(){
  //Deal with error
})

Creo que obtiene la excepción porque está usando el mismo cliente que está suscrito para publicar mensajes. Cree un cliente separado para publicar mensajes y eso podría resolver su problema.

Awemo
fuente
2

Si desea que esto funcione con socket.io 0.7 Y un servidor web externo, debe cambiar (además del staticProvider -> problema estático):

a) proporcionar el nombre de dominio en lugar de localhost (es decir, var socket = io.connect ('http://my.domain.com:3000');) en el index.html

b) cambiar HOST en app.js (es decir, const HOST = 'my.domain.com';)

c) y agregue sockets en la línea 37 de app.js (es decir, 'socket.sockets.on (' conexión ', función (cliente) {…')

dirkk0
fuente
0

según la solución @alex . si tiene un error como este según la mención de @tyler :

node.js:134
        throw e; // process.nextTick error, or 'error'

event on first tick ^ Error: Redis connection to 127.0.0.1:6379 failed - ECONNREFUSED, Connection refused at Socket.

entonces necesitas instalar Redis primero. mira esto:

http://redis.io/download

hbinduni
fuente