Adding Enterprise Features to a Socket.io Application with Kaazing WebSockets

Mike Dove

As we discussed in my prior blog post Meeting WebSockets for Real, WebSockets enable development of distributed event-driven applications, particularly ones that exchange messages.  Kaazing’s high-level client libraries substantially simplify this effort.  It is possible to add these capabilities to a new Web application in a matter of minutes.

However, what if you already have a NodeJS application and your application already exchanges messages using very popular Socket.io library?

Socket.io is a fantastic open source technology that is very easy to use and adopt. However, many critical requirements of enterprise applications may present some challenges when using socket.io.  Most enterprises require advanced security, enhanced scalability, fault tolerance, etc.

In addition, ‘out of the box’ socket.io does not have support for mobile clients that many enterprises mandate.  In these cases there is a need to transition to an enterprise-class platform and library for using Websocket.

In this article I want to discuss how easy it is to modify your existing code written with socket.io to work with Kaazing WebSockets using a publish/subscribe model.

Let’s First Start with a Working Socket.io App

Let’s start with my AngularJS implementation of the canonical TodoMVC application that uses socket.io. I had a lot of fun building that app. We are going to make the simple Todo application into a shared app so one user can see the changes of other users). In addition we can add a NodeJS server component to store the current state of the items; so new users will receive an initial update.  And to prevent potential race conditions when items are being edited by a user, we will simply disable the updating of items by other users; other more sophisticated code can be added later.

Let’s look at several sections of my application’s main controller (complete source on GitHub).

[sourcecode language=”javascript” gutter=”0″]
// Connect to pub-sub service
$scope.socket=io();

// Load initial data on connect
$scope.socket.on(‘connect’, $scope.loadData);

// Setup receive callback
$scope.socket.on(‘todomvc-snd’, $scope.processReceivedCommand);
[/sourcecode]

The first thing is to connect to socket.io:

[sourcecode language=”javascript” gutter=”0″]
$scope.socket=io();
[/sourcecode]

Once a connection is established, we send the request to the server to load any initial data .  We do this by sending the command “init” to “todomvc-rcv” socket together with the attached to the $scope client ID (server will send the reply to this command to all the clients – clients with different IDs will ignore it):

[sourcecode language=”javascript” gutter=”0″]
$scope.socket.on(‘connect’, $scope.loadData);

$scope.loadData=function(){
var msg={
command:’init’,
client:$scope.client
}
$scope.socket.emit(‘todomvc-rcv’, msg);
$log.info(‘Sent initialization!’);
}
[/sourcecode]

Now, we need to setup the callback method to receive messages from the server:

[sourcecode language=”javascript” gutter=”0″]
$scope.socket.on(‘todomvc-snd’, $scope.processReceivedCommand);
[/sourcecode]

$scope.processReceivedCommand is straightforward and just responds to individual commands.

[sourcecode language=”javascript” gutter=”0″]
$scope.processReceivedCommand=function(cmd){
$log.info(‘Received command ‘+cmd.command);
if (cmd.command===’insert’){

} else if (cmd.command===’remove’){

} else if (cmd.command===’update’){

} else if (cmd.command===’initdata’){

}
}
[/sourcecode]

Note, we are setting up callback on a todomvc-snd topic, while sending the messages to todomvc-rcv topic. I could not find the way to have socket.io send the data from one client to the others directly. To overcome this issue, the client will send the data to a server on the topic A and the server will retransmit the message to the other clients on the topic B.
Sending the data is pretty trivial. For example, to add the item:

[sourcecode language=”javascript” gutter=”0″]
var msg={
command:’insert’,
item: newTodo
}
$scope.socket.emit(‘todomvc-rcv’, msg);
[/sourcecode]

That was the client side of the application.  Let’s now look at the server side NodeJS component.  Besides the code to process the received message and serve static pages it also contains the part that connects to socket.io:

[sourcecode language=”javascript” gutter=”0″]
io.on(‘connection’, function(s){
console.log(‘a user connected’);
socket=s;
s.on(‘disconnect’, function(){
console.log(‘user disconnected’);
});
s.on(‘todomvc-rcv’, processMessage);
});
[/sourcecode]

Function processMessage contains the code to store the items that also includes send message calls to retransmit.

[sourcecode language=”javascript” gutter=”0″]
function processMessage(cmd) {
console.log(‘Command: ‘+ cmd);
if (cmd.command === ‘insert’) {

} else if (cmd.command === ‘remove’) {

} else if (cmd.command === ‘update’) {

} else if (cmd.command === ‘init’) {

}
}
[/sourcecode]

Similar to the client side code, sending messages is very simple.  If we want to send the message retCmd to everyone:

[sourcecode language=”javascript” gutter=”0″]
var retCmd = {
command: ‘initdata’,
client: cmd.client,
items: todos
}
socket.emit(‘todomvc-snd’, retCmd);
[/sourcecode]

If we want the message to be received to all the sockets except the one that sent it:

[sourcecode language=”javascript” gutter=”0″]
socket.broadcast.emit(‘todomvc-snd’,cmd);
[/sourcecode]

Now, we can run and test the application (detailed instructions).

Now… Adding the Socket.io Facade to Kaazing WebSockets

To make things easy, we are going to employ the Kaazing Gateway AMQP Edition since NodeJS has robust libraries for AMQP.  While the Kaazing Gateway comes pre-packaged with the Apache Qpid AMQP broker, it can easily be RabbitMQ or another AMQP-compliant broker.  Refer to GitHub for instructions for installing the Gateway.

Modifying client code

We are going to use Kaazing Universal Client Library for AngularJS to communicate with WebSocket Gateway.  This client library provides a high-level messaging abstraction that is independent of the underlying AMQP or JMS implementations.
Here are the steps we need to take

  • Modify app.js to include the Kaazing Universal Client Library service and create connection parameters contstants (see https://github.com/kaazing/tutorials/blob/develop/todomvc/js/app.js )

    [sourcecode language=”javascript” gutter=”0″]
    angular.module(‘todomvc’, [‘ngRoute’, ‘ngResource’, ‘KaazingClientService’]).
    .config(function ($routeProvider) {

    })
    .constant(‘todoMvcWebSocketConfig’, {
    URL: ‘ws://localhost:8001/amqp’,
    TOPIC_PUB: ‘todomvc’,
    TOPIC_SUB: ‘todomvc’,
    username: ‘guest’,
    password: ‘guest’;
    });
    [/sourcecode]

  • In the main controller source code
    • Connect to Gateway and establish all callbacks (replace $scope.socket=io() and all $scope.socket.on calls)

      [sourcecode language=”javascript” gutter=”0″]
      // Connect to WebSocket
      AngularUniversalClient.connect(
      ‘amqp, // use AMQP protocol
      todoMvcWebSocketConfig.URL, // connect URL (default is ws://localhost:8001/amqp)
      todoMvcWebSocketConfig.username,// use guest account
      todoMvcWebSocketConfig.password,// use guest pswd
      todoMvcWebSocketConfig.TOPIC_PUB, // use ‘todomvc’ as publishing topic
      todoMvcWebSocketConfig.TOPIC_SUB, // use ‘todomvc’ as subscription topic
      true, // No Local flag set to true which prevents the client to receive messages that it sent
      $scope.processReceivedCommand, // function for processing received messages
      function(e){alert(e);}, // function to process WebSockets errors
      $scope.logWebSocketMessage, // function to process WebSocket logging messages
      $scope.loadData // function that will be called when the connection is established
      );
      [/sourcecode]

    • Replace $scope.socket.emit with AngularUniversalClient.sendMessage

      [sourcecode language=”javascript” gutter=”0″]
      var msg={
      command: ‘insert’,
      item: newTodo
      }
      AngularUniversalClient.sendMessage(msg);
      [/sourcecode]

Note that we no longer need to use different topics for publishing and subscription.  The Kaazing WebSocket Gateway will send published messages to all subscribed clients without the need to retransmit them in the server code!

Server code changes

To preserve our server side code we are going to use the Kaazing Socket.io wrapper located in the node subdirectory of the todomvc implementation.

The wrapper hides the details of the use of AMQP protocol (which is used for communication with NodeJS) while exposing interface that mimics the one used in Socket.io.

With our socket.io wrapper, we only need to make two changes on the server:

  • Change both send and receive topics to “todomvc”
  • Remove the code that retransmits messages

Here is the final server code implementation.

Summary of Changes

If you were to run the diff:

app.js vs app-socketio.js:

  • Added declaration of the Kaazing Universal Client Service
  • Added constant for the connection parameters

todoCtrl.js vs todoCtrl-socketio.js

  • Added reference to Kaazing Universal Client Service
  • Added reference to the connection parameters constant
  • Replaced connection and callbacks for socket.io with a single call to initialize Kaazing Unviersal Client
  • Replaced Socket.io socket.emit functions with Kaazing Universal Client sendMessage cals

serverws.js vs serversocketio.js

  • Replaced declaration of io:

    [sourcecode language=”javascript” gutter=”0″]
    var io = require(‘socket.io’)(http);
    [/sourcecode]

    was changed to

    [sourcecode language=”javascript” gutter=”0″]
    var io=require(‘./node/socketioalt.js’)(‘amqp://localhost:5672’);
    [/sourcecode]

  • Removed all retransmit calls socket.broadcast.emit(“todomvc-snd”,cmd);
  • Changed the topics “todomvc-snd” and “todomvc-rcv” to “todomvc

Conclusion

Its very easy to upgrade a socket.io application to use the enterprise-class Kaazing WebSocket Gateway.  There are barely any source changes in your existing code.  Not only do you now get high-performance, advanced security and scalability features, Kaazing technology will extend your former Socket.io applications to work with mobile clients and many B2B and inside-the-firewall applications.

It’s a huge win.