måndag 17 juni 2013

Node.js


Admittedly, besides from a little dabbling with PHP a long time ago, I don't have much experience in writing server side applications in other frameworks than Node.js. So I don't have much to compare with. But I have to say I'm surprised by how quick and easy it is to write server side apps using Node.js.

For those who don't already know, Node.js is a software platform for server side apps, and it is built upon the same Javascript engine (called V8) as used in Google Chrome. Thus Node.js makes it possible to execute Javascript code on the server. Before Node.js, Javascript was regarded solely as a client-side language.

Perhaps the first benefit that comes to mind with this is that we can now share code between the client and server implementations. Let's say you are making a simple multiplayer game in Javascript, and you want the server to be in charge of the game logic, but you also want clients to run game logic to be able to predict the game state while waiting for updates from the server. Without running the same language on server and client this could be quite a hazzle, and even small differences between the client's and the server's engine state calculations could lead to large confusing differences in state. Thus it's a really good advantage to be able to run the exact same logic on both sides. (Besides, obviously you wouldn't have to implement the game engine twice.)

This leads us to another aspect of Node.js: Node.js apps are typically single-threaded, meaning one single thread on the server handles all the requests made from multiple clients. Client-server work should be designed to be asynchronous, i.e. typically the client sends a request to the server and the server will call the client back shortly later using a callback function. As a benefit of being single-threaded, the overhead needed to serve a new connecting client is very low: the server does not need to create any new process or allocate any new memory, and it does not have to do any context switching to serve multiple clients.

As a result, one server app can typically handle a large number of clients without bogging down. On the other hand, this means that it's bad to spend a long execution time serving one client as work will not be going on in parallell. Going back to the multiplayer game example above, it's then actually a bad idea to run game engine code on the server unless the executions can be made quickly. I guess Node.js apps should be designed to do just short operations. (I have heard there are some attempts in using something like webworkers to spawn threads for longer running operations, but I have not tried it out.)

Also, serving clients in one process is obviously dangerous if some code is misbehaving: if one operation crashes, the whole server app goes down. As an example, I found out that the file system call to read from a file will throw an error if the file does not exist. Forget to write exception handling for that, and you'll watch your server go down in flames when one single client cannot find the file it is looking for.

Anyway. For my experiments so far Node.js has been nice to work with, and the benefits have greatly outweighed the limitations. Knowing a bit of Javascript it's usually very quick, and does not require much code, to achieve quite a lot. There are tons of open-source packages you can use to speed up your work, and a package manager (aptly called NPM, Node Package Manager) to help you installing things.

So far, using Node.js, I have written:
  • One file server, which I use as a web server on my Linode machine, i.e. to serve files over http at www.mattiaserlo.com
  • One "session server", which uses socket.io to handle communication between clients. I'm using this for simple multiplayer games and chat-type apps.
  • A few RESTful services, that I used for testing to store and retrieve data from the server.
I'm running all of those on my rented Linode server.

To get started using Node.js on a Linux machine, go to http://nodejs.org/ and download an archive, then unpack and install like this, using shell:

tar xzvf node-v0.6.7.tar.gz (or whichever version you have downloaded)
cd node-v0.6.7
./configure
make
make install

You can run Node.js apps on your local computer using localhost, although it's much more fun to host your stuff on a real server that is always accessible by your friends. If you don't already have access to a server, I recommend renting one: see my earlier posting, http://mattiaserlo.blogspot.jp/2013/06/virtual-private-server.html

Finally, as an example, let me show how easy it is to create a file server using Node.js and a few packages:

Assuming you have installed Node.js (see above),

1. Do "npm install node-static"
2. Do "npm install http"
3. Create a file named myfileserver.js, containing this:

var static = require('node-static');
var http = require('http');
var file = new(static.Server)('/var/www'); // The folder which you want to serve files from
var httpServer = http.createServer(function(request, response) {
request.addListener('end', function() {
file.serve(request, response);
}
}).listen(80); // The port number your server will listen to requests on

4. Run the server like this: node myfileserver.js

Now you can connect to your server from another computer and fetch files from /var/www.

To run your fileserver as a service, instead of from a blocking shell call, break the Node.js execution above and create a file named myfileserver.conf, containing this:

description "My node.js server"
author "Me"

start on started mountall
stop on shutdown

# Automatically Respawn:
respawn
respawn limit 99 5

script
export HOME="/root"
exec /usr/local/bin/node /node/myfileserver.js >> /var/myfileserver.log 2>&1
end script

post-start script
# Optionally put a script here that will notifiy you node has (re)started
# /root/bin/hoptoad.sh "node.js has started!"
end script

The exec line above assumes you have stored myfileserver.js in a folder called /node.

Store the conf file in /etc/init on your machine, then you can start and stop your service whenever you want, like this:

start myfileserver
stop myfileserver

Inga kommentarer: