101

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.

retrodrone
  • 5,850
  • 9
  • 39
  • 65
guilin 桂林
  • 17,050
  • 29
  • 92
  • 146

8 Answers8

123

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!"
Alfred
  • 60,935
  • 33
  • 147
  • 186
  • why do you need `const client = redis.createClient()` in the root of app.js? – Akasha Apr 14 '12 at 18:40
  • 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. – Alfred Apr 14 '12 at 22:05
  • @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? – x_maras Jun 12 '12 at 12:06
  • 5
    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. – Alfred Jun 12 '12 at 16:00
  • @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) https://github.com/dinostheo/redis_pbsb_node.js_socket.io_Hello_world – x_maras Jun 13 '12 at 13:28
  • 1
    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 :$. – Alfred Jun 14 '12 at 03:08
  • @Imme22009 Sorry but I haven't done anything on this since then https://github.com/dinostheo/redis_node_pbsb – x_maras Mar 27 '13 at 10:11
  • 1
    I think subscribe.on should be outside the socket.on('connection') block to avoid multiple subscribes/ – zubinmehta Jul 03 '15 at 09:37
  • Why did you emphasize not to install npm as root? – Phantom007 Aug 31 '18 at 17:08
29

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.

nak
  • 3,077
  • 3
  • 27
  • 35
  • Here when we add data to the redis, should I run publish pubsub to get notification of insert? – IshaS Jul 07 '15 at 10:09
  • 1
    @IshaS if that's what you need to do, yes. You should also look into transactions if you need to run multiple commands atomically: http://redis.io/commands/exec – nak Jul 07 '15 at 13:27
  • @nak This worked like charm in one GO :) Some users may need to install 'double-ended-queue' if not installed already. – Manjeet Jul 22 '17 at 18:53
  • 1
    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`. – Liosha Bakoushin Feb 12 '20 at 13:35
8

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

nelsonic
  • 31,111
  • 21
  • 89
  • 120
  • do you have this example with express? – Gixty Jul 27 '15 at 17:02
  • @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: https://github.com/nelsonic/learn-hapi – nelsonic Jul 28 '15 at 05:53
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.

Awemo
  • 875
  • 1
  • 12
  • 25
2

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.

ma11hew28
  • 121,420
  • 116
  • 450
  • 651
2

If you want to get this working with socket.io 0.7 AND an external webserver you need to change (besides the staticProvider -> static issue):

a) provide the domain name instead of localhost (i.e. var socket = io.connect('http://my.domain.com:3000'); ) in the index.html

b) change HOST in app.js (i.e. const HOST = 'my.domain.com'; )

c) and add sockets in line 37 of app.js (i.e. 'socket.sockets.on('connection', function(client) { …' )

dirkk0
  • 2,460
  • 28
  • 34
1

Update to the code:

staticProvider

now renamed to

static

see migration guide

0

according to @alex solution. if you have an error like this one as per @tyler mention:

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.

then you need to install Redis first. check this out:

http://redis.io/download

Community
  • 1
  • 1
hbinduni
  • 1,170
  • 17
  • 27