Summer of Sockets Part 5: Node, Nanomsg and Websockets

Z

eroMQ has its fair share of quirks and oddities. It manages everything in a global state, requires things be manually grouped into `Contexts`, allocates a thread per context (making it not thread-save) , transports are baked into the library, and so on. It can be a bit clunky to work with at times. As a result, one of the original developers on the ZMQ project, Martin Sustrik, started a new project that evolved into a complete re-write / re-realization of the ZMQ project, called nanomsg.

Nanomsg aims to resolve many of the underlying short comings of the zeromq library, but remain compliant with the ZMTP spec. It provides many of the messaging patterns ( which are refereed to as scalability patterns ) as found in ZMQ - like Pair, Pipeline, Pub-Sub and Req/Rep.

It also introduces two new patterns, Bus - which is basically the peer-to-peer pattern ( many to many ) and Survey - which is, much like pub-sub with replies. What is even more interesting, is that the transport mechanism is plugin based, opposed to zermq where transports are vertically integrated, or baked in. This allows for others to create transport outside of the library itself. What is even more interesting, is that HTML5 Websockets is one of the transports that ships with nanomsg making the browser a viable and active participant in the network topology. The node project for nanomsg comes with a simple example of using websockets - It basically boils down to this:

// server.js
// modified example found in the nanomsg project
'use strict';
const nanomsg = require('nanomsg');
const socket = nanomsg.socket('pair');
const http   = require('http');

// Websocket
socket.bind('ws://0.0.0.0:3001');
socket.on('data', function(msg){
    pair.send(`received message ${'' + msg}`);
})

// Serve a webpage
http.createServer(function( req, res ){
  fs.createReadStream( 'index.html' ).pipe( res )
}).listen( 3000 )

Make a new Websocket, and give it a nanomessage socket type mapping, and wait for data.

<!DOCTYPE html>
<html>
<head></head>
<body>
   <div id="response"> 
      <span> </span> 
   </div>
   <input type="text">
   <button>send</button>
   <script>
      var ws = new WebSocket('ws://127.0.0.1:440',[
         'pair.sp.nanomsg.org'
      ])

      ws.onmessage = function( e ){
          var reader = new FileReader() // handle binary messages
          reader.addEventListener('loadend', function(){
             var result = reader.result;
             document.querySelector('span').textContent = result
          });
          reader.readAsText( e.data );
      }

      var button = document.querySelector('button');
      var input  = document.querySelector('input');

      button.addEventListener('click', funtion( e ){
         ws.send(input.value);
         input.value = '';
      },false)
   </script>
</body>
</html>

Done. 2-way communication ( via the Pair socket type ) over websocket using nanomsg. The server code ( the important part anyway ) is only 5 lines. Event The node package is less than 500 lines. In contrast to something like socket.io which has 3 external dependent libraries ( a parser, adapter, and underlying engine) and also requires a client side library before you can even make a connection.

dəˈvīs -n., --noun

A piece of software that connects one or more sockets together to deliver messages; An application

Additionally, all nanomsg sockets are duplex streams. So, in the case of a proxy or forwarding device, we really don't need to do anything with the messages, could just pipe them to another nanomsg socket. Similar to the pair socket type in ZeroMQ, nanomsg will not load balance multiple clients; which means our example only works with the first open connection. But you could just as easily use pub/sub. req and reply socket types or open a second socket to achieve something more practical.

Chat

Chat is really a sync problem more than it is a real-time problem

Chat is a rather common use case for websockets these days. Unlike most, I like to think of chat as a sync problem more than it is a real-time problem. Person A writes to a log, Person B and Person C get a copy of the log, or an incremental update of the log. Pub-Sub makes these updates rather easy to do, Lets tweak our example to make use of the Pub-Sub model.

We need to change the socket type on the server.

// server.js
const socket = nanomsg.socket('pub');

We also need to change the websocket mapping in the browser code

var ws = new WebSocket('ws://127.0.0.1:440',[
   `pub.sp.nanomsg.org'
]);

Yes, change both to pub. It feels backwards, but the websocket transport mapping needs to match that of the server and the library does the rest. But pretty simple. Now every browser that connects will get every message sent in a fire and forget style. As you might be thinking, subscribers can't send messages! And you would be right.

This breaks our 2-way communication. Unfortunately, nanomsg websocket protocol does not double as an http server as you are probably used to with libraries like socket.io. However, this fits in with the way nanomsg works in general. Transports are pretty agnostic as to the use case - browser or not, it is just another participant in the messaging pattern and network topology.

Topology (təˈpäləjē) -n., --noun.

  1. the way in which constituent parts are interrelated or arranged.

We could open up another socket connection to handle one way push back to the server, or start an http server - As far as I can tell, you are going to need two ports or multiple servers to handle each scenario. Out of curiosity, I have implemented a silly chat app using Kafka as the keeper of the log. Basically an HTTP POST is used to write to the log, and Kafka notifies any consumers ( the same app in this case ) which publish the message down the websocket. This could easily be expanded to use Kafka topics to mirror additional rooms and IMs and tying server instances to partitions and it would scale out pretty easily.

Chat. Done.

Nanomsg builds on many of the great idea that came out of ZeroMQ and opends the door for more interesting ideas with far less work. As we've seen here, the amount of code required to include client side applications as a member of otherwise complex networking applications is rather easy to do.
You can find the code for this example here along with the rest of the summer of sockets posts

summer of sockets nanomsg websocket node zeromq