How to use redis PUBLISH/SUBSCRIBE with nodejs to notify clients when data values change?

79,421

Solution 1

OLD only use a reference

Dependencies

uses express, socket.io, node_redis and last but not least the sample code from media fire.

Install node.js+npm(as non root)

First you should(if you have not done this yet) install node.js+npm in 30 seconds (the right way because you should NOT run npm as 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

Install dependencies

After you installed node+npm you should install dependencies by issuing:

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

Download sample

You can download complete sample from mediafire.

Unzip package

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

What's inside 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>

Start server

cd pbsb    
node app.js

Start browser

Best if you start google chrome(because of websockets support, but not necessary). Visit http://localhost:3000 to see sample(in the beginning you don't see anything but PubSub as title).

But on publish to channel pubsub you should see a message. Below we publish "Hello world!" to the browser.

From ./redis-cli

publish pubsub "Hello world!"

Solution 2

here's a simplified example without as many dependencies. You do still need to npm install hiredis redis

The node JavaScript:

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

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

...put that in a pubsub.js file and run node pubsub.js

in redis-cli:

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

which should display: pubsub: Hello Wonky! in the terminal running node! Congrats!

Additional 4/23/2013: I also want to make note that when a client subscribes to a pub/sub channel it goes into subscriber mode and is limited to subscriber commands. You'll just need to create additional instances of redis clients. client1 = redis.createClient(), client2 = redis.createClient() so one can be in subscriber mode and the other can issue regular DB commands.

Solution 3

Complete Redis Pub/Sub Example (Real-time Chat using Hapi.js & Socket.io)

We were trying to understand Redis Publish/Subscribe ("Pub/Sub") and all the existing examples were either outdated, too simple or had no tests. So we wrote a Complete Real-time Chat using Hapi.js + Socket.io + Redis Pub/Sub Example with End-to-End Tests!

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

The Pub/Sub component is only a few lines of node.js code: https://github.com/dwyl/hapi-socketio-redis-chat-example/blob/master/lib/chat.js#L33-L40

Rather than pasting it here (without any context) we encourage you to checkout/try the example.

We built it using Hapi.js but the chat.js file is de-coupled from Hapi and can easily be used with a basic node.js http server or express (etc.)

Solution 4

Handle redis errors to stop nodejs from exiting. You can do this by writing;

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

I think you get the exception because you are using the same client which is subscribed to publish messages. Create a separate client for publishing messages and that could solve your problem.

Solution 5

Check out acani-node on GitHub, especially the file acani-node-server.js. If these links are broken, look for acani-chat-server among acani's GitHub public repositories.

Share:
79,421

Related videos on Youtube

guilin 桂林
Author by

guilin 桂林

Python and web

Updated on July 29, 2020

Comments

  • guilin 桂林
    guilin 桂林 almost 4 years

    I'm writing an event-driven publish/subscribe application with NodeJS and Redis. I need an example of how to notify web clients when the data values in Redis change.

  • PaulM
    PaulM almost 13 years
    Wow, this looks pretty sweet!
  • Akasha
    Akasha about 12 years
    why do you need const client = redis.createClient() in the root of app.js?
  • Alfred
    Alfred about 12 years
    you don't need to use const at all. var could be used as well and maybe I should have instead because const is only available in the newer javascript engines. Furthermore this line ensures we are connected to redis server which we use in this example.
  • x_maras
    x_maras almost 12 years
    @Alfred I tried your example and it seems that it never goes in the socket.on('connection', function(client) { block. I tried to print something there and never does. if I remove this part I can normally publish and see the message in the console. Also when I run pubsub.js the following are printed on the console: Express server listening on port3000 info - socket.io started Do you maybe have an idea of what might be wrong in my case?
  • Alfred
    Alfred almost 12 years
    The sample is very old so not up to date with latest socket.io/express modules and maybe even node.js. I would try to update code. There is also another huge problem with this code that it opens another redis connection for each connected user. That should only be on instead. I have to work first, but after that I try to update the code.
  • x_maras
    x_maras almost 12 years
    @Alfred I think that I managed to fixed it. Not sure if my code is good (it's my second day that I work with node.js). I 've put it on github(I wanted to play a little bit with it as well) github.com/dinostheo/redis_pbsb_node.js_socket.io_Hello_worl‌​d
  • Alfred
    Alfred almost 12 years
    Very good. I still think there is room for some improvements which when I have time I will put online. But right now I am really working hard :$.
  • x_maras
    x_maras about 11 years
    @Imme22009 Sorry but I haven't done anything on this since then github.com/dinostheo/redis_node_pbsb
  • zubinmehta
    zubinmehta almost 9 years
    I think subscribe.on should be outside the socket.on('connection') block to avoid multiple subscribes/
  • IshaS
    IshaS almost 9 years
    Here when we add data to the redis, should I run publish pubsub to get notification of insert?
  • nak
    nak almost 9 years
    @IshaS if that's what you need to do, yes. You should also look into transactions if you need to run multiple commands atomically: redis.io/commands/exec
  • Gixty
    Gixty almost 9 years
    do you have this example with express?
  • nelsonic
    nelsonic almost 9 years
    @Gixty we wrote the example using Hapi.js because all the other examples out there use Express.js ... as mentioned in the post, its trivial to port it to any other Node.js framework (simply pass in the express app/listener to the chat.js init code) and it works exactly the same. p.s: if you are new to Hapi.js see: github.com/nelsonic/learn-hapi
  • Manjeet
    Manjeet almost 7 years
    @nak This worked like charm in one GO :) Some users may need to install 'double-ended-queue' if not installed already.
  • Phantom007
    Phantom007 over 5 years
    Why did you emphasize not to install npm as root?
  • Liosha Bakoushin
    Liosha Bakoushin about 4 years
    It also worth to mention that if you want to use wildcards, for example, subscribe to pubsub/* just add p to the example: replace subscibe with psubscribe and message with pmessage.