In my previous post I did some benchmarks for NodeJS vs Java’s NIO. For benchmarking I wrote my custom event driven loop handling, and parsing HTTP connections; and what else you can expect! Hell broke loose when the NodeJS fans read that. They had sort of weird comments and objections. From those all the weirdness I learned a few things. Things that were pretty simple and straight forward. This post will be my last post on all I have to say about NodeJS, and Java (Yes, I won’t be comparing them again!), and other than just theory (as usual) I will present the experiments. While reading this post please bear in mind, I am not a technology maker or an Omega of any big platform; I am one of the developers among the race of curious ones, who when encounters a new technology readily accepts it, experiments with it and then places it in the right shelve (just like a weapon) for the right use case. The reasons behind my last blog post include clarifications (to my self and people around) about pros and cons, a solid idea of what’s going on inside, and what if I never had NodeJS on hand (more precisely how Facebook and Twitter guys handle so much load). I totally believe in greedy approach to problems, where I say “To optimize the bigger problem, you have to optimize the subpart of the problem”.
I know I annoyed alot of people just by comparing Apples and Bananas (Java and NodeJS). Both languages have a ridiculously different paradigm (OO vs Prototypal) and totally different maturity level (15 years of engineering on Java). So this post is to put both tools in “same situation” and experiment what are the results. Some people thought JIT, and JVM has nothing to do with IO based system, well I won’t say anything in reply to it yet. Let’s proceed with first benchmark.
This time I am taking the same NodeJS with Cluster (Thanks to suggestion in previous post), to add multi-core CPU utilization support (Like always you can suggest if there is a better option). Also I changed the code to do some really basic and simple processing, using a counter with timestamp system that just prints a simple message with number of hit counts, and timestamp. For Java I have a special arrangement this time; every body last time objected that my code was pretty unfair to the amount of processing NodeJS (http module) has to do, so I am making it fair this time. Netty NIO is the new contender in the ring for Java this time; with its fully implemented HTTP support, NodeJS people now cannot object on anything regarding the event loop and parsing system. The code gets divided into four classes now main launcher, pipeline factory, handler (NodeJS guys would be interested in this one), and a singleton. Pretty fair pieces of codes for both sides, both utilizing 1 worker per CPU core (I have a dual core), and having full fledged HTTP protocol support. Lets have look at benchmarks again! I used concurrency level 1000 and NodeJS managed to get only 5.2K req/sec [Raw Dump], a increase of 200 over last time (That’s pretty sweet you wasted my time again!). Java Netty NIO on other hand manages to handle 7.3K req/sec [Raw Dump], a decrease of 700 over the last time (now you know why I have decided not to write on both of these again); thats still over 1.7 times faster.
A basic application
Somebody suggested me to put them in a real scenario (do something with some storage and serve JSON and all typical stuff) and then do the benchmarks. Well taken, I picked Redis (Good enough for speed) for both of them and made an application where hit ‘counter’ now lies in Redis along with the hash ‘channel’ that contains a mapping of ‘timestamp’ => ‘counter value’. On each request I will check if the ‘counter’ exists; if no then I initialize it to 1 by set operation and set the hash value by hmset, if yes I increment the existing value and save that incremented value to map with hmset. Pretty basic and simple to understand (Like assigning a unique id to a user and making him join a chat channel). It can’t get any simpler and practical. So I am putting NodeJS redis (used npm install hiredis redis; its all steroids!) client’s “asynchronous coolness” with Java’s uncool asynchronous Jedis and Jackson for JSON serialization. Later on I discovered lettuce for Netty NIO as well, but I was too lazy to re-do benchmarks. Using lettuce will definitely improve the performance (or will it?). Let NodeJS have that advantage this time and see what results are we looking at. Here is the updated NodeJS code, and updated handler class and singleton class of Java.
Now back to benchmarks; running benchmarks with concurrency 1000 on NodeJS had a knockout, I started receiving connection reset by peer, super long delays (even with total 10000 requests it seemed like taking hours) and Redis running out of file numbers (Yes I’ve opened my limits and done everything possible but it was not working). So we have to lower the concurrency a little bit (since all this is running on same machine); with concurrency level 100 I was able to run benchmarks so I took it as standard point for Java too. With concurrency 100 NodeJS was able to make 1.1K req/sec [Raw Dump], and Java Netty NIO still managed 1.7K req/sec [Raw Dump]; after concurrency level 100 I tried shots on concurrency level 500 and results were pretty much same [NodeJS ~0.9K max][Java ~1.4 max]!
So in conclusion: