Scalable Sockets with SockJS and Redis

Joseph DotsonOne of the biggest issues with socket servers is that traditionally they do not scale. The requirement to have a consistent TCP connection makes things very difficult when you want to scale. If one connection sends a message to Server A, how does that message get to a user on Server B?

The solution is to integrate with some centralized pub/sub system that is external to the socket connection system. Unfortunately, this means that if your socket layer is already heavily based on pub/sub, you will end up duplicating efforts.

SockJS is a very simple socket server that allows you to send and receive messages. Since the mechanism for sending messages is so simple, you will need to write your own abstractions to get more functionality. Fortunately, this means that there will be no fighting with existing layers or duplicating effort.

Redis is primarily a cacheing server, however it also has a very simple to use sub/pub you can use with Nodejs.

Step one of setting this up requires you to make a simple express server. It doesn’t need anything more than an index.html file to use, and I won’t talk about the front-end side of this in this article.

var sockjs_opts = {sockjs_url: "http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js"};
var sockjs_chat = sockjs.createServer(sockjs_opts);
sockjs_chat.on('connection', function(conn) {
    var red = util.getRedis();

    red.on("message", function(channel, message){
        conn.write(message);
    });

    conn.on('data', function (e) {
        red.subscribe('client_' + e)
    })
});

sockjs_chat.installHandlers(server, {prefix:'/sock'}); // endpoint

This code is very simple, but is the core of a basic socket server that can be extended, and more importantly scaled.

sockjs_opts is a configuration object for the server, where sockjs_chat is the actual server.

util.getRedis(); is a method that has the below body

function getRedis() {
    return redis.createClient();
}

The event “message” that the code adds a handler to makes any message that any topics currently subscribed to trigger the function.

Much like the Redis event, the socket connection subscribes to a “data” event. This is triggered any time data is sent from the client. In this example it’s only used to pass in a client identifier.

red.subscribe does exactly what it sounds like, it subscribes to whatever topic that is passed in to it. Causing the “message” events to tigger on publishes to that send to that topic.

sockjs_chat.installHandlers(server, {prefix:'/sock'}); simply installs the connection on that route. Once this line is done the server is technically complete.

While the code above is very simple, it is very powerful, and solves a lot of issues people run into when using sockets and trying to scale. I use this code in my own project, when I replaced SignalR. If you have a large number of servers and a lot of messages, I would suggest replacing Redis with a dedicated service bus, however for the most part Redis should be good enough to get started, and also serves the purpose of a cache.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s