NodeJS is pretty popular these days, so I took some time to have a in-depth look at it and analyze the pro/cons of using it against another server-side technology.

The first issue I can see is not technical : you have to rewrite all your programs and libraries by passing callback methods. This is a bit annoying since if you want to make three async requests you have to write the following :



async1 ( function ( result1 ) { async2 ( function ( result2 ) { async3 ( function ( result3 ) { } ) ; } ) ; } )

You also have to adopt the same style as soon as you make a regular function call that will itself make an async call.

There is one possibility however to convert this code into the following :

var result1 = async1 ( ) ; var result2 = async2 ( ) ; var result3 = async3 ( ) ;

This involves using Continuation Passing Style transformation (CPS). I am still considering adding CPS to Haxe but if you're using Javascript there is already some CPS tools such as NarrativeJS.

Once the syntax issues are solved, let's see how NodeJS is implemented :

NodeJS has a single event-loop thread that waits for I/O on sockets and files. Once some data is ready, it then triggers the corresponding event method and waits until it returns before waiting again for more I/O events. Since all I/O operations are non-blocking, it will make sure that everything runs correctly as soon as the input is available without any lock, and without the developer having to deal with multithreading.

I think that the last part is the most important : it's very hard to get a multithreaded application work nicely, and much more hard to ensure that the exclusive locks it will have to use will not cause any starving.

Starving in servers can be defined as waiting for some lock while everything else we need to run our program is available, "everything" being usually CPU time and input data.

While it's a nice goal to provide a threadless server technology so developers can focus on the application logic and still get good performances, I think the way NodeJS does it is wrong.

In fact, having a single thread to handle the callbacks is fine as long as you have only short-running events that are most of the time waiting for I/O (networking, filesystem or database). While this remain true for most web services calls, you sometimes have some requests that will need actually some CPU calculus. In that case, NodeJS unique thread will lock your entire server during the time this request is running, until it terminates or wait for more I/O.

So while you can run several NodeJS instances (one per CPU core) to ensure best CPU usage for your hardware, this is not true parallelism as long has one request can lock the others while it's being processed.

Criticizing being always easy, let's see how we can make things different :

When implementing the Tora application server for Neko, I had the same requirements as NodeJS : parallelism without having to write multithreaded applications. The solution I used was to have one entry-thread that waits for incoming connections and then dispatch the sockets to a waiting queue. One of the allocated worker threads will then process the request.

In order to make sure that there is no multithread issue, each time a worker thread needs to call a "module" (which consists in a NekoVM bytecode .n file), it will first fetch one from the common Tora cache, and if not found allocate a new one. Each module having its own memory space, it is then possible to have several instances of the same code running in parallel as they would have done in several processes.

Once the event is processed, we can then put back the module into the cache, ensuring this way that you will have allocate in the worse case scenario as much module instances as you have worker threads.

But since we are in the same process, if a long CPU-consuming request is blocking one thread, further requests will only wait until another thread is free. And if you have more threads than CPU cores, you can actually have a server that will have quite a good level of fairness and thus reduce starving.

Additionally, running into the same process allows for easy memory sharing and other goodies.

Currently, Tora can be used in two ways :

behind a HTTP server for request/answer mode. Most of the real-time is spent waiting for MySQL answers, we could here use an alternate MySQL driver that would perform async I/O à la NodeJS

as a realtime socket server : in that case after performing the first request, the socket will be given to a unique thread waiting for I/O à la NodeJS but the callback will be handle by one of the worker threads

I wish NodeJS and other single-thread server technologies could take into account important things such as fairness and starving.