Video icon 64
Learning to code? Skill up faster with our practical video courses. Start your free trial today.
Advertisement

Node.js: Better Performance With Socket.IO and doT

by

If your site relies on data sent to clients, you can boost its performance using client-side templates and WebSockets. In this tutorial, I will show you how.


Introduction

We will begin by transforming a preexisting application to be faster and more durable under high traffic. I will show you how to locate pieces of code which could be modified to improve the app. Start by downloading the sample application that I prepared. It's really simple, it displays random posts rendered on the server, but it will do just fine for this tutorial.


Step 1: Preparation

Unzip the app that you downloaded previously into a preferred folder or location and run it:

start_the_app

Now navigate to http://localhost:8080/ and take a look. You should see only a button. You'll also need to be using a browser with developer tools, so you can see the size of the HTTP requests. Open the developer tools and go to the Network tab. Then, click the button a couple of times and take a look at the size of the /getpost request:

getpost_size

It's only about 830 bytes right? Well, imagine that this site gets really popular and one million users want to see this post. It gets to about 830 megabytes. Per post! Not so small a number any more.


Step 2: Inspection

In this step I will show you how to find code that can be modified to boost up the app. In the previous step, you've found the request used to get the post. You will now have to find how it is served in the code. Open up the index.js file in your favorite code editor. Now go to the lines 16-20:

var postTemplate = dot(fs.readFileSync('./post.dot'));

app.get('/getpost', function (req, res) {
	res.end(postTemplate(posts[Math.floor(Math.random() * 3)]));
});

Here it is! First, the post's template is compiled into the postTemplate variable. Then, on the /getpost GET request the template is served. Nothing fancy, just a classic approach to the problem. We'll need to change this to improve its performance.


Step 3: Setting-Up Socket.IO

To begin the improvements, first, install Socket.IO. In your terminal type:

npm install socket.io

Wait for the command to complete. Require it in the code by adding the following line after all requires in index.js:

var sio = require('socket.io');

Now you will have to change the Express set-up, to work with Socket.IO. First, after the app definition, add this:

var server = require('http').createServer(app);
var io = require('socket.io').listen(server);

server.listen(8080);

And remove the last line in this file:

app.listen(8080);

You need to do this because Socket.IO requires the HTTP Server to work, not the Express app.

Now, if you run the app you should see something like this in your terminal:

start_with_socketio

Step 4: Client-Side Template

To start boosting up the app you will need to save the compiled template on the client-side. Javascript files are cached, so it will be downloaded only once. To compile the template, go to http://olado.github.io/doT/index.html and scroll down to the Usage section. Because there will be no need to compile the template every time the user visits your site, you can just add the compiled function to the code. Open the post.dot file and paste its content into the Template field like this:

paste_the_template

Now copy the content of the field and paste it into the static/main.js file before all code in there. Change or remove the anonymous function name and assign it to the postTemplate variable like this:

var postTemplate = function (it) { ... }

Now go to the index.js file and remove unused lines, because you will not be compiling templates on the server-side any more:

var dot = require('dot').template;
var fs = require('fs');

...

var postTemplate = dot(fs.readFileSync('./post.dot'));

The post.dot file can also be deleted too.


Step 5: From AJAX to WebSockets

Instead of using AJAX to communicate with the server, we will now use WebSockets. It's best to do this using Socket.IO, because the WebSocket API itself does not provide any fail-overs in case the user's browser doesn't support it. The server is already set-up, so now we will need to connect to it. First, add this in the head of the static/index.html file (before main.js):

<script src="/socket.io/socket.io.js"></script>

Next, open the static/main.js file and after the template definition, add this code:

var socket = io.connect();

It will connect to the Socket.IO server (notice that you've added Socket.IO's script to the page earlier). Since the server is on the same host as the client, you don't need to provide any parameters to the function. Now, you need to add an event listener to the socket, so we know when the post arrives. Add this just after the previous line:

socket.on('getpost', function (data) {
	$('button').after(postTemplate(data));
});

As you can see, the callback looks the same as the success callback in jQuery's $.ajax() method but it's running the template function on the data first. Now, replace the $.ajax() call with this:

socket.emit('getpost');

This line will notify the server that the user wants to display a new post. Of course, in a real-world app, the server would send the posts when they are published.


Step 6: Sending the Posts

For now, the client can connect to the server and request a post, but the server will not send anything yet. You need the server socket to listen to the getpost event and respond with a random post. Go to the index.js file and change it to this:

app.get('/getpost', function (req, res) {
	res.end(postTemplate(posts[Math.floor(Math.random() * 3)]));
});

To this:

io.sockets.on('connection', function (socket) {
	socket.on('getpost', function () {
		socket.emit('getpost', posts[Math.floor(Math.random() * 3)]);
	});
});

This will make the server attach the getpost handler to every client that connects to it and respond him with a random post. Now you can run the app again and try it out. Then go to the developer tools in your browser again, go to the Network tab, filter it out so you can only see WebSockets and click on the one that is visible there. You should see the data sent between the browser and the server, and notice that it is much smaller then it was with the server-side template and AJAX (the length is in bytes):

getpost_ws_size

Conclusion

As you can see, using WebSockets and client-side templates can (and probably will) improve the performance and durability of your app. I'm sure there are plenty of sites that could be boosted with this technique and I hope that you will use it to provide a better user experience, both for your mobile and desktop users.

Advertisement