Performance Zone is brought to you in partnership with:

Marc is a senior consultant, architect, and thought leader at Shine Technologies, a boutique software development and consulting company based in Melbourne, Australia. He has 18 years experience building large-scale high-performance enterprise applications and still has a passion for the development of excellent software. He regularly speaks at meetups and conferences, and blogs about technology at blog.shinetech.com. Marc is a DZone MVB and is not an employee of DZone and has posted 4 posts at DZone. You can read more from them at their website. View Full User Profile

Performance Comparison Between Node.js and Java EE

10.23.2013
| 75407 views |
  • submit to reddit

Performance Comparison Between Node.js and Java EE For Reading JSON Data from CouchDB

Node.js has impressed me several times with high performance right out of the box. In my last Node.js project it was the same: we beat the given performance targets without having to tweak the application at all. I never really experienced this in Java EE projects. Granted the project was perfectly suited for Node.js: a small project centered around fetching JSON documents from CouchDB. Still I wanted to know: how would Java EE compare to Node.js in this particular case? 

The project had to meet the following performance targets: 150 requests/second at 200ms average response time. I am not a performance guru, 200ms response time sounded pretty fast, and my feeling was that we would have to tweak the application to reach those goals.

A separate team ran performance tests against our application, and when the results came back the application actually had exceeded all performance targets: 200 requests/second at 100ms average response time. That was much better than the targets. I was quite amazed that Node.js was outperforming the requirements by such a margin, and all of this without any performance optimization.

I asked myself: Is this really a good performance given the functionality of the application? Is Node.js just magically fast? What would the performance be if we would’ve gone with the established platform of Java EE?

I really couldn’t answer that question. Many Java EE applications I have worked on had response times that felt more like 1000ms, but they had more complex functionality than our Node.js application did. The core of our application only pulled out JSON documents by ID from a single table in a CouchDB database. No complex SQL, no table joins, and no data manipulation. I don’t know how a Java EE application would perform given those requirements. So I went out to answer the question: Can the perceived performance of Node.js vs. a traditional Java EE system be backed up by hard performance tests?

To answer this question I designed a set of performance tests to be run against both a Java EE application and a Node.js application, both backed by the same CouchDB database, and looked at how the two systems compared.

Preparation

I ran the the same performance tests against both a Node.js application and a Java servlet application. Both applications used the same backend as our original Node.js application: CouchDB. I used CouchBase Single Server version 1.1.3. I created 10.000 sample documents of 4KB each with random text. The test machine was a iMac with 2.4 GHZ Intel Core 2 Duo, 4 GB RAM, and Mac OS X.

I used Apache JMeter running on a separate machine as a test driver. The JMeter scripts fetched random documents from each application at various levels of concurrency.

Java EE

The Java servlet was run on an Apache Tomcat version 7.0.21, default configuration running on Java 1.6. The database driver was CouchDB4J version 0.30. The driver has no caching options available, so no configuration was done.

The following Java code is a servlet that fetches a document from CouchDB by id and forwards the data as a JSON object.

package com.shinetech.couchDB;
 
import java.io.IOException;
import java.io.PrintWriter;
 
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.apache.log4j.Logger;
 
import com.fourspaces.couchdb.Database;
import com.fourspaces.couchdb.Document;
import com.fourspaces.couchdb.Session;
 
@SuppressWarnings("serial")
public class MyServlet extends HttpServlet {
  Logger logger = Logger.getLogger(this.getClass());
  Session s = new Session("localhost",5984);
  Database db = s.getDatabase("testdb");
 
  public void doGet(HttpServletRequest req, HttpServletResponse res)
    throws IOException {
    String id = req.getPathInfo().substring(1);
    PrintWriter out = res.getWriter();
    Document doc = db.getDocument(id);
    if (doc==null){
      res.setContentType("text/plain");
      out.println("Error: no document with id " + id +" found.");
    } else {
      res.setContentType("application/json");
      out.println(doc.getJSONObject());
    }
    out.close();
  }
}

I ran the JMeter tests against this servlet at various levels of concurrency. The following table shows the number of concurrent requests, the average response time, and the requests that were served per second.

Concurrent Requests

Average Response time (ms)

Requests/second

10

23

422

50

119

416

100

243

408

150

363

411

What can be seen is that the response time deteriorates as the number of concurrent requests increases. The response time was 23 ms on average at 10 concurrent requests, and 243 ms on average at 100 concurrent requests.

The interesting part is that the average response time has an almost linear correlation to the number of concurrent requests, so that a tenfold increase in concurrent requests leads to a tenfold increase in response time per request. This makes the number of requests that can be handled per second is pretty constant, regardless of whether we have 10 concurrent requests or 150 concurrent requests. At all observed concurrency level the number of requests served per second was roughly 420.

Node

The Node.js application ran on Node.js 0.10.20 using the Cradle CouchDB driver version 0.57. The caching was turned off for the driver to create equal conditions.

The following shows the Node.js program that delivers the same JSON document from CouchDB for a given ID:

var http = require ('http'),
  url = require('url'),
  cradle = require('cradle'),
  c = new(cradle.Connection)(
          '127.0.0.1',5984,{cache: false, raw: false}),
  db = c.database('testdb'),
  port=8081;
 
process.on('uncaughtException', function (err) {
  console.log('Caught exception: ' + err);
});
 
http.createServer(function(req,res) {
  var id = url.parse(req.url).pathname.substring(1);
  db.get(id,function(err, doc) {
    if (err) {
      console.log('Error'+err.message);
      res.writeHead(500,{'Content-Type': 'text/plain'});
      res.write('Error' + err.message);
      res.end();
    } else {
      res.writeHead(200,{'Content-Type': 'application/json'});
      res.write(JSON.stringify(doc));
      res.end();
    }
  });
}).listen(port);

The numbers for Node.js system were as follows:

Published at DZone with permission of Marc Fasel, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Peter Gwiazda replied on Fri, 2013/10/25 - 2:14am

I don't belive you've actually tested anything.

First of all I think that "raw" Node.js should be way faster than JavaEE server returning responses under heavy load. It's like motorcycle vs. train. They have very different model and functionality. In simple servlet there is still much more under the hood than in Node.js.

You should rather compare Node.js to Vert.x which is a JVM equivalent to Node.js with very similar programming model but multithreaded. Or take a look at Play!, Spray or at least asynchronous JEE Servlet 3.0.

What is more the CouchDB server is the slowest part of your benchmark. You actually tested how fast is CouchDB connector for Java and for Node.js.

Still this is a good starting point for discussion.

Marc Stock replied on Fri, 2013/10/25 - 9:44am

You do realize that Tomcat is not an EE server, right?

John Connor replied on Sat, 2013/10/26 - 2:42pm

 Is this some kind of joke ? Calling "Java EE" a simple servlet with a doGet method is totally ridiculous.

Brice Leporini replied on Sun, 2013/10/27 - 2:38am

First of all, It seems you're not benchmarking two different technologies, but two different designs as your JS code is fully asychronous and the Java one is fully synchronous.

Now it would be great to know a little more about the Tomcat configuration: did you use a blocking IO connector or a non blocking one? In case of a blocking one, what was the thread pool size?

Would you mind sharing the hole benchmark (CouchDB + NodeJS + Java + JMeter) on a githut project


Kris Hofmans replied on Wed, 2013/10/30 - 5:38am

Oh boy ... Where's the EE part?

Seriously, this is just a Java vs Node.js comparison.

For the EE part thrown in some transaction management, preferably XA where the Node.js integrates with couch and SAP over a JCO connection, something like that ... 

This is just a very small test of a huge stack that not only has many years ahead in performance tuning but also in features. Features that, yes, will cause slowdowns for "simple" things like this, yet save you months of hard work reinventing the wheel.

There is a car analogy as well, look at what they do for racing cars, weight reduction, stiffer suspension, throw out navigation, speakers, etc, goes really fast, but would you want to drive it on a daily basis? I hope the answer is no.

Rico D'amore replied on Wed, 2013/10/30 - 10:13am

The author states a few times that this is not really a useful test, and that we should take this comparison with a grain of salt.  That said: can this article be anything but nothing?  I imagine the author has experience writing enterprise applications right?  And the folks at D-Zone have some experience in editing?  How did this article make it to my inbox?  Ugh.

Matt Parker replied on Wed, 2013/10/30 - 10:23am

I agree with the others where this fails to be a J2EE app.

Also, I would think there would be a better way to do connection pooling than giving every Servlet instance its own Database instance. This is probably the major bottleneck.

Robert Brown replied on Wed, 2013/10/30 - 11:56am

I look at it like this.  For what Node.js was intended to do it does it well, better than a full up untuned J2EE stack, so probably, would a simple socket interface that handles bytes directly.  But like many others have pointed out, the devil is in what you are trying to do.  If all you are doing is returning pure data like a firehose, Node likely will be a good option.  If you are doing things like security beyond a very basic model, multi tenancy, batch processing, business logic etc.  It may not be the best, not even because of performance but because of maintainability.  It's easier to write JS code that is difficult to understand than it is to write Java code that is difficult to understand.  Not to say that people can't write hard to maintain and understand Java, it's just that it gives you a few more ways to keep on track (compiler errors and typing come to mind).  

Tomcat is a good general purpose server it handles a lot of different things in a decent way that is at least decently maintainable.  Node.js handles many things faster but IMHO it opens a door to less maintainable code as the complexity goes up which IMHO is at least AS important as ultimate performance. 

Pick the right tool for the job at hand not the "RIGHT" Tool.  


Marek Jankowski replied on Wed, 2013/10/30 - 12:45pm

Oh please! Node.js actually runs on Google's V8 engine that actually COMPILES JavaScript on runtime. It's NOT interpreted! Please check your sources before writing articles.

Source: https://developers.google.com/v8/design 


Chris Pratt replied on Wed, 2013/10/30 - 1:53pm in response to: John Connor

Technically, the Servlet spec was one of the first added to the Enterprise Edition.  Just because it doesn't contain EJB's doesn't mean it doesn't use Enterprise Edition functionality.

  (*Chris*)

Marek Jankowski replied on Wed, 2013/10/30 - 2:24pm in response to: Chris Pratt

Yep, it's part of the JEE specs: http://en.wikipedia.org/wiki/Java_Servlet


Navyn Rajoo replied on Wed, 2013/10/30 - 4:10pm

Thanks for the test. It seems that even synchronously the java servlet (the container, jvm, java) is handling it pretty well and close. I'm pretty sure if there was something like synchronous node.js it wouldn't perform this well. I would love to see how the servlet handles this if the code is broken down asynchronously. Actually pure servlets are really fast without some of the heavy bloated over-engineering some have tacked onto them. Thanks, keep it coming.

Franz Roleda replied on Wed, 2013/10/30 - 6:50pm

Hi there, Is there any expected time before Node.js gets adapted?Although I'm quite adept on using it(JS), I'm leaning more to using parse.com for my backend needs

I think couch db needs something similar to parse.com's REST API

I know it has its own what I mean is a similar structure wherein you can "query" from the request URL itself

Marek Jankowski replied on Wed, 2013/10/30 - 7:14pm in response to: Robert Brown

Well said

Ram Krishnan replied on Wed, 2013/10/30 - 11:43pm

Node.js is not interpreted language any more. Its used google chrome V8 Javascript Engine as runtime to execute node.js. so V8 Javascript do same exactly what JVM do, compile the code to native code. so one sense both are compiled language. 

Robert Brown replied on Thu, 2013/10/31 - 9:22am in response to: Ram Krishnan

While it gets compiled when it is interpreted there is some extra overhead in that it checks, creates and compiles the classes on the fly when it discovers a change.  I'd be interested to see what happens behind the scenes when a delete is used on a property or method.  Does a new class get instantiated or is it just marked somehow.  If you were to use a static architecture with few if any of the dynamic aspects of JS you should have very similar execution to a JVM (except for the fact that it has to check if the property exists before every write).  

But, for example, a loop like this may be a lot slower. 

function HashClass(){};

var obj new HashClass();
for (var x = 1;x < 10000; x++){
    obj['a' + Math.random(x)] = x;
}

basically as this loop executes it would have to interrogate the current hidden class for obj and see if it has the 'a' + x property.  If it doesn't it would need to create a new class and compile and patch the current system.  

There's probably some kind of optimization that V8 does to make this not as intrusive since hashmaps are a pretty standard programming technique, but it does mean you should take a close look at portions of your code that do this kind of hashmapping when you are looking for performance issues, for example to generate indexes for documents or word counts etc.  

That said I haven't done any testing so I could be completely wrong, just something to keep in the back of your head when looking for performance gains.  In fact reading this blog has made me think about checking some of our Chrome js code to see if we can do something to speed things up.

Thanks
Bob

Marek Jankowski replied on Thu, 2013/10/31 - 10:47am in response to: Robert Brown

My understanding is that when a new property or method is detected a new instance of the class is created "inheriting" the original class (or actually referencing it).

I'm not sure what you mean by "delete is made on a property or method"?

My understanding is that once a new property is introduced, it can't be deleted from the class instance.

Marek Jankowski replied on Thu, 2013/10/31 - 11:51am in response to: Marek Jankowski

Eye bleeding artcile V8 on the compilers (yes, there are 2 compilers involved!) : https://docs.google.com/document/d/1hOaE7vbwdLLXWj3C8hTnnkpE0qSa2P--dtDvwXXEeD0/pub

Just quickly read through it, does not seem to cover the "deleted property" use case.

Robert Brown replied on Thu, 2013/10/31 - 3:42pm in response to: Marek Jankowski

I'm talking about the js case of. 

function NewClass(){};

var a = new NewClass();

a.newProperty = 'prop';

console.log(a.newProperty); //logs 'prop'

delete a.newProperty;

console.log(a.newProperty); //logs undefined;

a.newFunction = function(){console.log('test');}

a.newFunction();//logs 'test'

delete a.newFunction 

a.newFunction(); //ERROR newFunction undefined.

What happens to the underlying class?  

Thanks
Bob

Marek Jankowski replied on Thu, 2013/10/31 - 7:29pm

It seems like it simply ignores the delete statement when it comes to the class instance: https://groups.google.com/forum/m/#!topic/v8-users/zE4cOHBkAnY


Robert Brown replied on Thu, 2013/10/31 - 9:23pm in response to: Marek Jankowski

Interesting so using delete in Chrome or Node.js can cause it to go into "slow mode" for that object. 

Definitely need to review the few places we use delete. 

Thanks

Bob

Gary Bisaga replied on Mon, 2014/04/14 - 7:51am

Marc may not have done a perfect job, but I like his way of doing it a lot more than y'all's way of not doing it.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.