Como usar o redis PUBLISH / SUBSCRIBE com nodejs para notificar os clientes quando os valores dos dados mudam?

99

Estou escrevendo um aplicativo de publicação / assinatura orientado a eventos com NodeJS e Redis. Preciso de um exemplo de como notificar os clientes da web quando os valores dos dados no Redis mudam.

guilin 桂林
fonte

Respostas:

123

OLD usa apenas uma referência

Dependências

usa express , socket.io , node_redis e, por último, mas não menos importante, o código de amostra do media fire.

Instale node.js + npm (como não root)

Primeiro você deve (se ainda não fez isso) instalar node.js + npm em 30 segundos (da maneira certa, porque você NÃO deve executar o 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

Instale dependências

Depois de instalar o node + npm, você deve instalar as dependências emitindo:

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

Baixar amostra

Você pode baixar uma amostra completa do mediafire .

Descompacte o pacote

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

O que há dentro do zip

./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

Melhor se você iniciar o Google Chrome (por causa do suporte para websockets, mas não é necessário). Visite http://localhost:3000para ver uma amostra (no início você não vê nada além de PubSubtítulo).

Mas no publishcanal, pubsubvocê deve ver uma mensagem. Abaixo publicamos "Hello world!"no navegador.

De ./redis-cli

publish pubsub "Hello world!"
Alfred
fonte
por que você precisa const client = redis.createClient()na raiz do app.js?
Akasha de
você não precisa usar const. var também pode ser usado e talvez eu devesse, porque const está disponível apenas nos motores de javascript mais recentes. Além disso, essa linha garante que estejamos conectados ao servidor redis que usamos neste exemplo.
Alfred
5
O exemplo é muito antigo, então não está atualizado com os módulos socket.io/express mais recentes e talvez até com o node.js. Eu tentaria atualizar o código. Há também outro grande problema com este código que abre outra conexão redis para cada usuário conectado. Isso só deve ser ativado. Tenho que trabalhar primeiro, mas depois tento atualizar o código.
Alfred
1
Muito bom. Ainda acho que há espaço para algumas melhorias que quando tiver tempo colocarei online. Mas agora estou realmente trabalhando duro: $.
Alfred
1
Acho que subscribe.on deve estar fora do bloco socket.on ('conexão') para evitar vários assinantes /
zubinmehta
26

aqui está um exemplo simplificado sem tantas dependências. Você ainda precisanpm install hiredis redis

O nó JavaScript:

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

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

... coloque isso em um arquivo pubsub.js e execute node pubsub.js

em redis-cli:

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

que deve exibir: pubsub: Hello Wonky!no nó em execução do terminal! Parabéns!

Adicional 23/04/2013: Também quero observar que quando um cliente se inscreve em um canal pub / sub, ele entra no modo de assinante e fica limitado a comandos de assinante. Você só precisará criar instâncias adicionais de clientes redis. client1 = redis.createClient(), client2 = redis.createClient()portanto, um pode estar no modo de assinante e o outro pode emitir comandos DB regulares.

nak
fonte
Aqui, quando adicionamos dados ao redis, devo executar o publish pubsub para obter a notificação de inserção?
IshaS
1
@IshaS se é isso que você precisa fazer, sim. Você também deve examinar as transações se precisar executar vários comandos atomicamente: redis.io/commands/exec
nak
@nak Funcionou perfeitamente em um GO :) Alguns usuários podem precisar instalar o 'double-ended-queue' se ainda não estiver instalado.
Alex
1
Também vale a pena mencionar que se você quiser usar curingas, por exemplo, assine para pubsub/*apenas adicionar pao exemplo: substitua subscibepor psubscribee messagepor pmessage.
Liosha Bakoushin
7

Exemplo completo do Redis Pub / Sub ( bate-papo em tempo real usando Hapi.js e Socket.io)

Estávamos tentando entender a publicação / assinatura do Redis (" Pub / Sub ") e todos os exemplos existentes estavam desatualizados, eram muito simples ou não tinham testes. Então, escrevemos um bate-papo completo em tempo real usando Hapi.js + Socket.io + Exemplo de Redis Pub / Sub com testes ponta a ponta !

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

O componente Pub / Sub tem apenas algumas linhas de código node.js: https://github.com/dwyl/hapi-socketio-redis-chat-example/blob/master/lib/chat.js#L33-L40

Em vez de colar aqui ( sem qualquer contexto ), encorajamos você a verificar / experimentar o exemplo .

Nós o construímos usando Hapi.js, mas o chat.jsarquivo é desacoplado do Hapi e pode ser facilmente usado com um servidor http básico node.js ou expresso (etc.)

nelsônico
fonte
você tem esse exemplo com express?
Gixty
@Gixty, escrevemos o exemplo usando Hapi.js porque todos os outros exemplos por aí usam Express.js ... como mencionado no post, é trivial portá-lo para qualquer outro framework Node.js (simplesmente passe o app expresso / ouvinte do código init chat.js) e funciona exatamente da mesma. ps: se você for novo no Hapi.js, consulte: github.com/nelsonic/learn-hapi
nelsonic
4

Trate os erros do redis para impedir que o nodejs saia. Você pode fazer isso escrevendo;

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

Acho que você obteve a exceção porque está usando o mesmo cliente que está inscrito para publicar mensagens. Crie um cliente separado para publicar mensagens e isso pode resolver seu problema.

Awemo
fonte
2

Se você quiser que isso funcione com socket.io 0.7 E um servidor da web externo, você precisa mudar (além do staticProvider -> problema estático):

a) forneça o nome de domínio em vez de localhost (ou seja, var socket = io.connect ('http://my.domain.com:3000');) no index.html

b) alterar HOST em app.js (ou seja, const HOST = 'my.domain.com';)

c) e adicionar sockets na linha 37 do app.js (ou seja, 'socket.sockets.on (' conexão ', função (cliente) {...')

dirkk0
fonte
1

Atualize para o código:

staticProvider

agora renomeado para

estático

veja o guia de migração

Alex Mikhalev
fonte
0

de acordo com a solução @alex . se você tiver um erro como este conforme a menção 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.

então você precisa instalar o Redis primeiro. Veja isso:

http://redis.io/download

Hbinduni
fonte