Realtime service

In this section, the realtime service will be created with Node.js and a minimal HTML client will visualize the data. After that, the realtime service will be packaged and run as a container.

Writing the server

Start by creating a new folder, open a terminal and navigate to that folder. Then enter the following command:

npm init

Accept all the defaults the command throws at you. At the end, a file called package.json will be created. Our application requires several packages. Install them with the following command:

npm install express path socket.io redis --save

The packages express, path, socket.io and redis are installed and added to package.json which now looks like below.

{
  "name": "socket",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.15.2",
    "path": "^0.12.7",
    "redis": "^2.7.1",
    "socket.io": "^1.7.3"
  }
}

Now create a file index.js with the following code:

var express = require('express');
var path = require('path');
var app= express();
var server = require('http').Server(app);
var io = require('socket.io')(server, { origins: 'localhost:8888'});
var port = 8888;

// use our Azure Redis server
var subscriber=require("redis").createClient(6380, "<Name_of_Redis>.redis.cache.windows.net", 
    {auth_pass: '<Auth_Pass>',
     tls: {servername:  '<Name_of_Redis>.redis.cache.windows.net'}});

// when a client connects to http://<servername>/ just emit 200 (OK)
app.get('/', function(req,res,next){
    res.sendStatus(200);
});

// log a message when a new socket.io connection comes in
io.sockets.on('connection', function(socket){
    console.log("Connection from " + socket.id);

    // when the client sends a 'channel' message with the name of a Redis channel as contents
    // join the client to a 'room' with the channel name
    socket.on('channel', function(channel){
        console.log('Channel message from client for: ' + channel);
        socket.join(channel);
    });
});

// when a message comes into a Redis channel, output the message to all sockets
// joined to a 'room' with the channel name
subscriber.on("pmessage", function(pattern,channel, message){
    console.log("Message from " + channel + " was: " + message);
    io.sockets.in(channel).emit('message', message);
});

subscriber.on('error', function(err) {
    console.log('An error occurred ' + err);
});

subscriber.psubscribe("*");


server.listen(port, function() {
    console.log('Listening on port ' + port);

});

If you have never worked with socket.io or even Node.js, the code above can be just a bit tricky. The comments are hopefully making it clearer. A couple of notes though:

  • We need a web server for clients to connect to; Express provides that functionality and we selected port 8888; we could have also used the built in http module; later we will modify this code to get the port number from an environment variable
  • We use the concept of 'rooms' so that a socket.io client can join a room that has a name that corresponds to a Redis channel; in our case, the client would join the 'particle' room so that we can emit the contents of the Redis channel 'particle' to such a client

Run the above code (with the name of Redis server and key filled in) with:

node index.js

After the message Listening on port 8888 you should see output like below:

Clearly, the pattern subcribe worked and you see the messages in the particle channel (and others) as they come in. The socket.io server is running as well but that is pretty boring without a client.

Writing a minimal client

It is time to check if the above server-side code works! We will write an extremely minimal web page that serves as the client. The web page uses a bit of JavaScript and jQuery. Save the HTML below as index.html in the same folder that contains index.js.

<html>
    <head>
        <script src="/socket.io/socket.io.js"></script>
        <script src="https://code.jquery.com/jquery-1.11.1.js"></script>
    </head>
</html>
<body>
    <p>Welcome to the Socket.io Test!</p>
    <ul id="messages"></ul>
    <script>
        var socket = io();
        socket.emit('channel','particle');

        socket.on('message', function(msg){

            message = JSON.parse(msg);
            time = new Date(message.published_at)
            $('#messages').append($('<li>').text("Temperature was "+ message.data + " at " + time));
        })
    </script>
</body>

Naturally, you will need to include socket.io and jQuery in your script. As you can see, we load the socket.io.js library from the server that served this piece of HTML. This is possible because we serve the above HTML from our Node.js application. That required a slight code change for the code that serves the / route. Instead of

app.get('/', function(req,res,next){
    res.sendStatus(200);
});

it should now be

app.get('/', function(req,res,next){
    res.sendFile(__dirname + '/index.html');
});

With the above change to the code, index.html is served when a user requests http://localhost:8888 (if you are running index.js on your local machine).

Back to the HTML! We need to connect to socket.io running on our server. If the HTML and socket.io come from the same server, you can just use io() to connect. After connecting (and without any error checking, ouch!) a channel message is emitted with particle as the channel name. This instructs the server to place this socket in a room with the channel's name (so particle). Now, whenever a new message ends up in the Redis particle channel; it will be emitted to the socket.io room called particle. The message that is emitted is just called message and when that event fires, the callback function is triggered.

Inside the callback function, it is trivial to display the channel data. The message contents is the JSON string that was sent to the Redis channel and it gets converted to a JavaScript object. The time is converted to a JavaScript date. With a bit of jQuery, the value (data field) and the time (published_at field) are added as a list item (<li>) to an unordered list (ul). Easy peasy right? Clearly, it would be much nicer to have some flashy dashboard but the intent of this section was to create a very minimal client that gets the job done.

results matching ""

    No results matching ""