Writing custom protocol for nanomsg

Custom protocol for nanomsg

Nanomsg is next version of ZeroMQ lib, providing smart cross-platform sockets for implementation of distributed architectures. Here you can find basic examples of included protocols (communication patterns). Lib is simple (written in pure C) and does not have any dependencies like boost. And as this is at least 3rd iteration from same author you can expect some quality/performance here.

This is kind of solution for the hell of writing of your own serious socket server. If you already had such experience you should understand the range of problems which are not so obvious at start. But here we expect to skip all such problems and go straight to processing messages. Lib handles automatic reconnection in case of link disconnects, nonblocking receiving/sending, sockets which can handle large set of clients, etc. All this seems like perfect solution for server-side inner transport of fast distributed architectures.

But i also want to try it outside. The basic communication patterns (PAIR, BUS, REQREP, PUBSUB, PIPELINE, SURVEY) may fit large set of inner server transport schemes, but there are some minor limits in current implementation for client side application. I mean limits of protocols, not the lib itself.

Unfortunately, current version of PUBSUB protocol does filtering on subscriber side. So ‘subscribing clients’ will receive all message flow and this is unbearable for me.

BUS protocol requires full-linked scheme:

nanomsg bus protocol

I expect BUS-like protocol to work in more sparse conditions.

As nanomsg is open-source lib under nice licence (MIT/X11 license) – first thought was to extend some of existing protocols to meet my needs.

Why new protocol?

As i wanted to try these smart sockets for external clients, to meet today’s reality i’m assuming each client has set of devices, which are connected simultaneously to some network service.

At first i aimed to create some complex routing protocol, but than came up with more simple approach: I want to create custom protocol as fusion of BUS and SUB/PUB protocols (here i refer it as SUBBUS).

Scheme:

SUBBUS Protocol Scheme 1

 

Black lines are initial connections. Coloured lines are messages. This scheme contains 2 clients Bob and John. John has 2 devices and Bob is geek, so he has 4 devices simultaneously connected to server node. Each message from client device goes to other devices of same client. You can look at this scheme as two BUS protocols separated by subscription.

This gives ability to perform instant cloud synchronisation, simultaneous operation from multiple devices and other various fun stuff.

Possible inner structure (there can other ways):

  • Each node has the list of subscriptions (socket option as list of strings) i.e. /users/john/ or /chats/chat15/.
  • Subscription filtering is done on sending side (This is important if you have large number of clients – each of them don’t have to receive all messages. Not only for saving bandwidth but also for security reasons.) So client should somehow send his subscription list to server (subscription forwarding). In case of reconnect this information should be also resent again. While subscriptions were not sent client should receive nothing.
  • Each message should contain routing prefix (header) i.e. /users/john/ or /chats/chat15/
  • Each node should have tree of connected client subscriptions which contains pipe lists as leafs. Sending operation uses this tree to send to subscribed range of clients.
  • Each message from client node should be transmitted to other nodes within same subscription (forwarding). This is done before server side processing and aimed to speed up message propagation between devices. Some optional filters can be added here.
  • [Optional] SSL-like encryption for each pipe
  • All this stuff should be as simple as possible

Its not too complicated to start writing your own protocol for nanomsg. The only problem is that lib is written in pure C – so you must be a bit ready for it. Go to src/protocols folder. It contains all protocols sources you can explore. Mostly they simply implement the list of given methods, which are described inside src/protocol.h:

So you can just clone some protocol as base foundation for your own – i took bus folder and cloned it to subbus. I renamed everything inside from ‘bus’ to ‘subbus’ using find/replace. In root src folder there is bus.h file which contains only list of consts for protocol access. You also need to clone it under your new protocol name (subbus.h in my case). Next steps are to add new protocol to makefile and socket types list.

Add to makefile.am:

Add protocol to /core/symbol.c

Add protocol’s socket types into supported list inside /core/global.c (don’t forget includes):

After that i grabbed one of examples for bus protocol from here and changed socket creation part:

After that sample should compile and work. If you failed to add your protocol copy to socket types list you will get Unknown protocol error.

Here is complete Dockerfile i use to build&run simple test. It gets latest nanomsg from github, modifies sources to include new protocol, copies protocol source from host, builds the lib and protocol test.

Note: the lib is still beta (0.5-beta, released on November 14th, 2014) so you could expect something yet not polished there. Inside script you could find the line which disables statistics as it has some blocking bug at the moment but i expect it to be fixed very soon as the fix was pulled already.

Docker is optional way to build this, of course, and you can modify this Dockerfile to simple client script. Don’t forget to change the name of your protocol.

Modifications i made

I will not paste here the cuts of source code as it will make the post too messy. This is plain old C so even simple things tend to be a bit longer there. So i will note some main steps of my implementation. Keep in mind that thats only my approach and everything can be done another way. 

I modified nn_xsubbus_setopt to set subscriptions (i use linked list to store the list of local subscriptions).

I have two trees to speed up all process of communication routing. First tree contains descriptions of client subscriptions by pipe id (nn_pipe*). Also it contains the flag if this node’s subscriptions were sent to this pipe for first time. To make this tree more balanced i use some hash of pointer to pipe as binary tree key.

This tree is used in nn_xsubbus_addnn_xsubbus_rmnn_xsubbus_out functions to synchronise subscription lists. nn_xsubbus_add is called when new pipe is connected and there we add new leaf into the tree. nn_xsubbus_out tells that pipe is writable so we can send our list of subscriptions to other side (if we have not already done it). nn_xsubbus_rm – pipe was removed.

Second tree is used for main sending operation and gives the list of pipes by subscription string key. As starting point i took triple tree where each node contains actual list of connected pipes. nn_xsubbus_send method splits header from each message and sends it to corresponding tree part.

When new message arrives inside nn_xsubbus_recv there is check of header, and if it starts from special mark of the list of subscriptions – we add this list into the second tree. If message is ‘normal’ there is sending to other pipes of same subscription (message forwarding as BUS protocol wants).

Note, that trees should work as persistent trees in multithread environment. I prefer some non locking structures here. Current implementation does not clean up chains of disconnected leafs (just removes the pipes) to achieve this simple way. Some tree rebalancing algorithm would be nice to add in future.

As test i slightly modified bus test sample to set subscription from argv[2] as socket option and prepend message by current subscription.

Here is the part of test output (for Bob):

As you can see there is bus between node2, node4, node5, node6.

I will post the sources here after i perform some tests with large set of clients, some stress tests and so on.