<< November 2014 | Home | January 2015 >>

A reactive and performant Spray + Akka solution to "Playing with concurrency and performance in Java and Node.js"

In my previous post I examined a fictitious trading engine and compared a Java based blocking solution to a Node.js based non-blocking solution. At the end of the post I wrote that:

I suspect that following the recent success of Node.js, more and more asynchronous Java libraries will start to appear.

Well such libraries already exist, for example: Akka, Spray, and this Mysql async driver.

I set myself the challenge of creating a non-blocking Java based solution using exactly those libraries, so that I could compare its performance to that of the Node.js solution created for the last article. The first thing you might have noticed is that these are all Scala based libraries, but I wrote this solution in Java even though it is a little less syntactically elegant. In the last article I introduced a solution based upon Akka whereby the trading engine was wrapped in an actor. Here, I have dropped Tomcat as the HTTP server and replaced it with Spray, which neatly integrates the HTTP server straight into Akka. In theory this should make no difference to performance, because Spray is NIO just as Tomcat 8 is, out of the box. But what attracted me to this solution was that overall, the number of threads is greatly reduced, as Spray, Akka and the async Mysql library all use the same execution context. Running on my Windows development machine, Tomcat has over 30 threads compared to just a few over 10 for the solution built here, or compared to Websphere or JBoss where there are hundreds of threads. The execution context is basically a pool of threads which run tasks that are given to it. Since all the libraries used in the solution presented here were non-blocking, the number of threads can be kept low and close to the theoretical optimum, so that as little context switching takes place as possible, making the process run efficiently.

The code written for this article is on GitHub. The first part of the program is the main method which starts up Spray and Akka:

Line 1 creates an actor system which is public so that I can access it from elsewhere, since it is used to access the single execution context which I want to use throughout the program. (In code where maintainability is an issue I would write something so that this object could be injected into the relevant parts of the program.) Line 5 then uses the system to instantiate an actor which is used to handle all HTTP requests for purchase and sales orders. Lines 7-11 just set up configuration data for the server. Lines 12 and 13 are where we then take the configuration and our actor and tell Akka IO use them and the HTTP module to send all HTTP requests as messages to our actor from line 5. Lines 15-17 are where I effectively setup a timer task which fires every 5 seconds to output some statistics. The important part here is to notice that I am not using Java's Timer to schedule the task since that just adds more unnecessary threads to my process. Instead I use the same execution context as Akka, so as few threads as possible are created.

Next is the actor for handling the HTTP requests:

Line 3 shows an example of how integrating Scala in a Java program can be ugly, but how you can sometimes hide away those ugly parts by adding your own abstractions. The HTTP actor which responds to HTTP requests has 3 jobs. The first job, on line 6, is where it creates a router which I shall describe below, and which it can use to delegate work to. The second job is to handle all new connections on lines 24-25 which tells Spray that this actor will also handle the actual requests and not only the connections. The third job this actor has is shown on lines 9-18 where the actor takes an HTTP request and delegates (routes) some work to another actor in the system.

This actor knows the HTTP model but the HTTP abstraction doesn't leak into the next layer of the system. Instead, the actor passes domain objects (or value objects or case classes or similar) onto the actors which encapsulate the trading engines. The construction of such a domain objects can be seen on lines 15 and 16, using data extracted from the HTTP request, e.g. on line 13, or out of say a JSON object in the request body. Spray contains useful directives which can help you extract the data from the request, and abstract a little away from HTTP, if that is what you want. Which domain object to construct depend on the REST-like interface which I have built and is handled on lines 9, 12 and 19. Had I used Scala, I could have written more elegant code using pattern matching on the HttpRequest object. The domain object is passed onto the trading engine by getting the router from line 6 to route the domain object to a suitable actor, on line 17. Last, but not least, line 18 is where the sales order request is acknowledged in an HTTP response which passes a JSON object back to the consumer, together with the unique ID assigned to the order, so that its status can be queried later (it gets persisted into the sales objects).

The next snippet shows how we partition the market and create a number of actors to handle requests in parallel.

This code is similar to what we did in the last article. In order to scale out and use more than one core concurrently, the market is partitioned by product ID and each trading engine runs concurrently for a different market partition. In the solution presented here, an EngineActor is created per partition and wrapped in a Routee on line 10. A map of actors keyed by product ID is also filled on line 14. The router is built using the routees and the map on line 19 and it is this which the HttpActor uses in the previous snippet, when delegating work. Note also line 17, which starts the trading engine contained in the EngineActor, so that it is up and running, ready to trade purchase and sales orders, when they are passed to these actors.

The EngineActor class isn't shown here explicitly since it is almost identical to the actors used in the last article, and it simply encapsulate a trading engine which handles all products from a particular market partition. Line 19 above uses a RoutingLogic to build the router, which is shown below:

The select(...) method on line 10 is called by the router when it receives an object which it must route to the correct actor. Using the map created in the previous listing, and the product ID obtained from the request, it is easy to find the actor which contains the trading engine responsible for the relevant market partition. By returning the routee which wraps that actor, Akka will pass the order object on to the correct EngineActor, which then puts the data into the model when that message is handled at a time when the trading engine is between trading cycles and the actor next checks its inbox.

OK, so that is the front end dealt with. The second major change that was required to the solution from the previous article, was the design of the method which persists sales after trading takes place. In the Java based solution I was synchronously iterating over each sale and sending an insert statement to the database and only processing the next sale once the database had replied. With the solution presented here, I chose to process the sales in parallel by fire off an insert request to the database and immediately moving to the next sale and doing the same. The responses were handled asynchronously within the execution context using a callback which I provided. I wrote the program to wait for the last insert to be acknowledged before trading continued with newly created purchase and sales orders which had arrived since the last trading session had started. This is shown in the following listing:

The persistSales(...) method is called by the trading engine after each trading cycle, and is passed a list of sales made during that trading cycle, and a callback function to be called once all the persistence is complete. If nothing was sold, then line 38 calls the callback immediately. Otherwise, a counter is created on line 5 which is initialised with the number of sales to be persisted. Each sale is persisted asynchronously on lines 7-15. Note how a Future is returned on line 15 and how we use another callback on lines 16-35 to handle completion of the future - there is no blocking done here, waiting for the future to complete! The above mentioned counter is decremented on line 25, once the sale is persisted, and once all sales are persisted, the callback passed into the persistSales(...) method is called. Note that the class JFunction1 used on line 16 is a shim allowing easier integration of Scala - the code is on GitHub at the link given above. Lines 21 and 22 show that I had a little problem with the async Mysql library that I used. It is still a beta, and doesn't seem to have a way to get hold of the generated (autoincrement) primary key of the sale. Note also line 35, where I pass in the execution context which Akka is using, so that the Future which handles completion of the insert statement is processed on one of the existing threads, rather than some new thread - again, keeping the total number of threads as low as possible.

This listing also shows an interesting problem, namely that the thread which calls the database to insert the data is not necessarily the same thread which might need to close the connection [1]. In normal Java EE and Spring there is often use of thread local storage (also see here). If you called through to a bean from the function handling the completion of the future, resources which are injected into it may not work, because the container cannot work out what the context is. Scala solves this problem using implicit parameters, which are passed into methods under the hood.

The listing above uses the PersistenceComplete callback, which is shown below on lines 14-16. It also uses a connection pool which is created using the following code. Yet again, the execution context which Akka uses is passed over to the async Mysql library, on line 10 below. Line 10 below also shows a non-default pool configuration where I allow a maximum queue size of up to a thousand. During load testing I was getting a lot of errors indicating that the pool was saturated, and increasing this value solved the problem.

The callback passed into persistSales(...) is shown in the next listing. The following code is hardly different from the original shown in the last article, except that it is now asynchronous in style. It is called once all sales are persisted and only then does the callback send a message (via its event listener) to the actor, on line 14 below. That message will normally be at the back of the inbox after a load of new purchase and sales orders. Each of those messages will be processed, leading to the trading engine model being updated with the new orders, before trading is recommenced.

The final code listing is the modification to the Node.js solution which was made so that it too would persist sales in parallel, rather than one after the other, as was the case in the last article.

Line 5 fetches a connection from the pool and the same connection is reused for all sales, "in parallel", and only released, i.e. returned to the pool, once the last sale is persisted, on line 19.

So, yet again, it's time to compare the solutions via some load tests. This time I chose to see what maximum rate of sales I could achieve with each of the following three solutions:

  • Case 1 - The solution presented here, namely Spray + Akka + the async Mysql driver,
  • Case 2 - The modified Node.js solution using persistence in parallel,
  • Case 3 - The original Tomcat non-blocking connector, but with synchronous persistence.

The cases were run using the hardware from the last article, with the trading engines running on the fast hardware and the database on the slow hardware, because that was the best setup to show how blocking I/O causes performance problems. For each case, there were three variables which I could adjust while tuning. These were:

  • Number of trading engines (either as actors or as child processes),
  • Time waited by client between calls to the server,
  • Number of concurrent clients.

The last two basically tuned the number of requests per second, since the connections were not kept open awaiting the trading results (see previous article). The results were as follows, with the best performance shown in bold.

Case 1 - Spray + Akka + async Mysql driver
# trading engines client wait time between calls concurrent clients sales per minute approx. CPU on trading hardware
8 100ms 60 42,810 25-35%
8 80ms 70 62,392 25-35%
8 60ms 80 75,600 30-40%
8 40ms 90 59,217 30-50%
10 60ms 80 too many DB connection problems
5 60ms 60 67,398 25-35%
6 60ms 80 79,536 25-35%


Case 2 - Node.js with persistence in parallel
# trading engines client wait time between calls concurrent clients sales per minute approx. CPU on trading hardware
8 200ms 30 6,684 40-50%
8 100ms 60 started to lag behind
8 100ms 40 17,058 25-35%
8 100ms 50 started to lag behind
12 100ms 50 20,808 45-60%
16 100ms 60 24,960 45-65%
20 100ms 80 32,718 45-70%
25 60ms 80 51,234 75-85%
30 50ms 80 22,026 75-85%
25 10ms 70 17,604 75-90%


Case 3 - Tomcat 8 NIO, with synchronous blocking persistence
# trading engines client wait time between calls concurrent clients sales per minute approx. CPU on trading hardware
4 200ms 30 9,586 5%
4 150ms 30 10,221 5%
8 200ms 30 9,510 5%

The results show that bolting a NIO connector onto Tomcat and thinking that you are non-blocking and performant is dangerous, as that solution underperformed by a factor of nearly 8 compared to the Akka solution. The results also show that by using non-blocking libraries and writing a non-blocking solution in Java, it is possible to create very performant solution in comparison to Node.js. Not only was the Java solution capable of some 50% throughput, it used less than half the CPU doing so.

Very important: please note that this is a result particular to the algorithms used here and my architecture, design and implementation. It is also dependent on using "non-standard" Java libraries, and indeed, the Mysql library I used was missing functionality, for example reading generated primary keys out of the result of an insert. Please do your own experiments for your use cases before drawing conclusions on relative performance of Java vs. Scala vs. Node.js!

A noteworthy point when comparing the variation of the number of trading engines: in Node.js it directly controlled the number of child processes, analagous to the number of threads; in the Akka solution it had no effect whatsoever on the number of threads in the system - that number stayed constant! In Akka solutions, varying the number of actors has an effect on the number of messages in their inboxes.

Further information pertaining to the use of Akka and Spray can be found at this good video. Please take the time to also quickly read up about the reactive manifesto. The Akka solution presented here is reactive because it is responsive (highest throughput of all three cases), resilient (Akka provides easy ways to deal with failure, although none were necessary here), elastic (it automatically scales out because Akka manages the thread pool size in the execution context and it scales up because Akka provides transparent location of actors), and it is message driven (due to using the actor model).

[1] The Mysql library used here doesn't require that the connection be closed and returned to the pool, as e.g. Apache database pool does. Doing so in fact causes problems! Leaving it open causes no problems, as proven by the load tests which I ran.

Copyright © 2014, Ant Kutschera

Social Bookmarks :  Add this post to Slashdot    Add this post to Digg    Add this post to Reddit    Add this post to Delicious    Add this post to Stumble it    Add this post to Google    Add this post to Technorati    Add this post to Bloglines    Add this post to Facebook    Add this post to Furl    Add this post to Windows Live    Add this post to Yahoo!

Playing with concurrency and performance in Java and Node.js

Imagine a simple market where buyers and sellers interested in the same product come together to trade. For each product in the market, buyers interested in the product could form an orderly queue, sorted on a "first come, first serve" basis. Each buyer could then approach the cheapest seller and trade, purchasing as much of the product from the seller as they wish for the price dictated by the seller. Should no seller be offering the product at a price low enough, the buyer could step to the side, giving the next buyer the opportunity to trade. Once all buyers have had the chance to make a trade, and after all products in the market have been through the cycle, the whole process can start again, after satisfied buyers and sellers leave and new ones take their place. In the internet age, there is no reason why buyers and sellers could not trade on a virtual platform, using this type of algorithm, from the comfort of their armchair. Indeed, trading platforms like this have existed for many years.

While basic, this type of problem becomes interesting when used to build a computer based trading engine. Simple questions pose challenges:
  • How could the market scale up across multiple cores?
  • How could the market scale out across multiple machines?
Inherently, the answers boil down to requiring some form of concurrency so that such a trading engine can scale. Typically I would jump into writing a Java based solution using perhaps an execution pool and the synchronized keyword to ensure that multiple threads updated the central model in an orderly fashion.

But recently I have started to play around with Node.js, and this platform is interesting for problems like that described above because it is a single threaded non-blocking platform. The idea is that the programmer has less to reason about when designing and writing algorithms, because there is no danger that two threads might want to access common data at the same time.

I took the time to model the market described above in JavaScript and the trading function is as follows (the rest of the JavaScript code can be found here [1]).

The code makes use of the Underscore.js library which provides a bunch of useful functional helpers, much like those added to Java 8 Streams.

The next step was to create a trading engine, which encapsulates a market as shown in the following snippet, which: prepares the market on line 1 by removing timed out sales where no suitable buyer and seller could be paired; runs through the trading process on line 3; notes statistics on line 6; and persists sales on line 8.

So far we haven't seen any code which is really interesting, except for line 8 above, where the sales are persisted. Sales are inserted into a table which contains indexes on the sale ID (an auto incremented primary key), the product ID, sales order ID and purchase order ID (which comes from the program). The call to the persistSale(...) function makes a call to a MySQL database and the library used makes use of non-blocking I/O when it calls the database. It has to do that because in Node.js there are no other threads available in the process and everything running in the process would block while waiting for the results of the database insertion. What actually happens is that the Node.js process fires off the insertion request and the rest of the code runs immediately, to completion. If you examine the rest of the JavaScript code, you'll notice that there is in fact no other code which runs after the call to the persistSale(...) function. At that point, Node.js goes to the event queue and looks for something else to do.

To make the trading engine useful, I decided to architect it as a standalone component in my landscape and expose its interface a simple HTTP service. That way I profit in a number of ways, for example having a deployable unit which can be scaled outwards by deploying it on several nodes in a cluster and having the back end decoupled from any front ends which I have yet to create.

The script named trading-engine-parent3.js has a dependency on a little web framework named express, and the relevant parts of that script are shown below:

Lines 8 and 12 call through to the engine and add a purchase order / sales order respectively. Exactly how is something we shall examine shortly. Line 16 shows an important choice that I made in the design, namely HTTP requests are not kept open while waiting for the result of a trade order. Originally I tried keeping the requests open, but during load testing I ran into classic dead locking problems. The market contained orders, but none with matching products, and the server wouldn't accept new requests after its TCP backlog filled (see also here), and so new purchase and sales orders could not be created by other clients and so the market didn't contain the necessary products for sales to flow consistently.

So, let's return to what happens after the sales of a trade are persisted. Since persisting is asynchronous, we provide a callback function on lines 8-20 of the previous script (trading-engine-loop.js) which handles the result by sending the appropriate events to the buyer/seller (lines 13-14) and making a call to setTimeout(loop, 0+delay) which tells Node.js to run the loop function after at least delay milliseconds. The setTimeout function puts this work onto the event queue. By calling this function, we allow Node.js to service other work which has been placed on the event queue, for example HTTP requests to add purchase or sales orders, or indeed calling the loop function to start trading again.

Because of the non-blocking asynchronous nature of the code that I have written for this Node.js solution, there really is no need for more threads. Except... how do we scale up the process and use other cores on the machine? Node.js supports creating child processes and doing so is very easy indeed, as shown by the following snippets.

Line 5 imports the API for working with child processes and we partition the market by grouping product IDs on lines 14-25. For each partition, we start a new child process (line 17) and register a callback for receiving data piped from the child process back to the parent on line 18. We stick a reference to the child process into a map keyed by the product ID on line 23 so that we can send it messages by calling for example: n.send(someObject). It is quite nifty how you simply send and receive objects and how they are transported as (presumably) JSON - it's very similar to RMI calls in Java.

With the solution presented above, the trading engine can be scaled vertically by adding child processes as well as horizontally by deploying the trading engine parent (including its web server) on multiple nodes and using a load balancer to distribute requests based on product ID to the correct node handling trading of that product.

In case you are wondering if a buyer can be present in multiple markets then the answer is yes of course - the markets are virtual, and buyers are not restricted by a physical location as they might be in real life :-)

What would the equivalent Java solution look like, and how would it perform? The complete Java code is available here [1].

Starting with the market and its trade() method, the Java code looks similar to the JavaScript version, using Java 8 Streams instead of the Underscore library. Interestingly, it is just about identical in number of lines of code or put more subjectively, maintainability.

As I wrote in my book a couple of years ago, it's normal to write multi-paradigm solutions these days, with functional programming being used for data manipulation, object orientation used for encapsulating say a buyer or seller or market, and as we shall see shortly, service and aspect oriented programming for glueing complex framework code into place to provide say a REST-like HTTP service. Next, the run method of the trading engine in Java, which trades as long as the engine is in a running state:

The Java design is a little different than the Node.js design in that I created a simple method named run which I will call once. It runs over and over so long as the boolean field named running is true. I can do this in Java because I can utilise other threads to do work in parallel to trading. In order to tune the engine, I introduced a short configurable delay at the end of each iteration, where the thread pauses. It was set to pause for 3 milliseconds during all the tests I did, which was the same used for the JavaScript solution.

Now I just mentioned using threads to scale out the system. In this case, threads are analogous to the child processes used in the Node.js solution. Just as in the Node.js solution, the Java solution partitions the market by product ID but instead of using child processes, the Java solution runs each trading engine (which encapsulates a market) on a different thread. Theory dictates that the optimum number of partitions will be similar to the number of cores, but experience shows that it also depends on how much the threads are blocked waiting for example to persist sales in the database. Blocked threads make room for other threads to run in their place, but too many threads reduces performance as context switching between threads becomes more relevant. The only reliable way to tune the system is to run several load tests and play with the variables like the number of engines in use.

The thread simply delegates running to the engine, which as shown above, runs in a loop until it's shut down.

For the Java solution, I used Tomcat as a web server and created a simple HttpServlet to handle requests to create purchase and sales orders. The servlet partitions the market and creates the relevant threads as well as starting them (note that a better way to do this would be to start the threads upon servlet startup and shutdown the engines when the servlet is stopped - the code shown is not production ready!). Line 15 of the following code starts the threads shown in the previous snippet.

The servlet handles purchase and sales requests as follows:

The relevant engine is looked up on line 8 and given the details for example to create a purchase order on line 14. Now it initially looks as though we have everything we need for the Java solution, but no sooner as I put load on the server, I was running into ConcurrentModificationExceptions and it was obvious what was happening: line 14 in the above snippet was adding purchase orders to the model in the engine at the same time that the market was say iterating over buyers purchase orders to determine which buyers were interested in which products.

It is exactly this kind of problem which Node.js avoids with its single threaded approach. It is also the kind of problem which can be really hard to fix in the Java world! The following tips may help:
  • Using the synchronized keyword to ensure synchronous access to the given (data) object,
  • In cases where you only need to read data and react to it, make a copy of the data,
  • Use thread safe collections for your data structures,
  • Modify the design.
The first tip can lead to deadlocks and is somewhat notorious in the Java world. The second tip is sometimes useful but involves the overhead of copying data. The third tip sometimes helps, but note the following comment contained in the Javadocs of java.util.Collections#synchronizedCollection(Collection):
Returns a synchronized (thread-safe) collection backed by the specified collection... It is imperative that the user manually synchronize on the returned collection when traversing it... Failure to follow this advice may result in non-deterministic behavior.
Using thread-safe collections is simply not enough and the problems related to the first tip don't go away as simply as one might hope. That leaves the fourth tip. If you take a look back at the code above, you will find a method named prepareMarket(). Why don't we store all purchase and sales orders in their own model until the trading engine which runs in its own thread gets to the point where it needs to prepare the market, and at that point, take all those open orders and add them to the market's model, before trading commences? That way we can avoid concurrent access from several threads and the need to synchronize on the data. When you look at all the Java source code you'll see that the TradingEngine does exactly this with the two fields named newPurchaseOrders and newSalesOrders.

The interesting thing about this kind of design is that it closely resembles the actor model, and the perfect library for Java already exists, namely Akka. So I added a second servlet to the application which uses Akka rather than threads, to show how it solves the concurrency problems. Described basically, an actor is an object which contains state (data), behaviour and an inbox of messages. No one has access to the state except for the actor, since it should be private to the actor. The actor responds to messages in the inbox and runs its behaviour based on what the messages tell it to do. The actor guarantees that it will only ever read and respond to a single message at any one time, so that no concurrent state modifications can occur. The new servlet creates new actors as follows, on line 13, using the actor system created on line 4. Note that just as above, this code is not production ready, as the actor system should be started when the servlet starts rather than within a static context as shown below, and it should be shut down when the servlet is stopped. Line 19 sends a message to the newly created actor to tell it to start the trading engine which it contains.

The actor class is shown next, with its data and behaviour being encapsulated in its instance of the trading engine.

You can see that the trading engine on line 4 of the actor class is private and only ever used when messages are received, for example on lines 12, 18 or 20. That way, the guarantee that no two threads can access it at the same time can be upheld, and importantly for us, there is absolutely no need to synchronize on the engine, meaning that our ability to reason about concurrency has been massively improved! Note that to allow messages in the inbox to be processed, the trading engine runs one trading session, and then a new "run" message is pushed to the inbox. That way, any messages from the HTTP server to add purchase/sales orders are first processed, before the trading continues.

It's now time to start looking at the performance of the designs under load. I had three machines at my disposal:
  • A "high performance" 6 core AMD processor with 16 GB RAM running Linux (Fedora Core 20),
  • A "medium performance" quad core I5 processor with 4GB RAM running Windows 7, and
  • A "low performance" Intel Core 2 Duo processor with 4GB RAM also running Linux.
Of all the possible deployment combinations I chose to run the following two:

# Load test client Trading engine Database
1 medium fast slow
2 medium slow fast

Before running the tests I made the prediction that the first case, running the trading engine on the fast hardware, would favour the Node.js solution, because Node.js should be better in situations where there is blocking. Since the database would be running on a slow machine, my hypothesis was that there would be a considerable amount of blocking compared to the other case with the trading engine running on the slow hardware.

The three machines were connected on a 100 megabit/second cabled network. The load test client was a custom built Java program which uses an execution pool to run 50 parallel threads making random purchase and sales orders, continuously. Between requests, the client pauses. The pause time was tuned so that the worse performing of the Java and Node.js processes could keep up with the load but were close to the tipping point where they started to lag, and is recorded below in the results. Results were not recorded before at least half a million sales had been persisted, and not before the throughput had stabilised (think hot spot optimisations). Throughput was measured using the number of rows inserted into the database, rather than the dodgy statistics which the programs output.

The results were:

Case 1 - 200ms client wait time, 4 trading engines
Fast trading engines, slow database
Synchronized Java Java with Akka Node.js
throughput (sales per minute) 5,100 5,000 6,400
average CPU on machine with trading engines <50% <40% 40-60%

Case 2 - 50ms client wait time, 2 trading engines
Slow trading engines, fast database
Synchronized Java Java with Akka Node.js
throughput (sales per minute) 32,800 30,100 15,000
average CPU on machine with trading engines 85% 90% >95%

In case one, the trading engines were not CPU bound. In case two, the trading engines were CPU bound, but the system as a whole performed faster than case one. In neither case was the system network bound, because I measured up to a maximum of 300 Kilobytes per second transfer speeds which is less than 3% of the network capability. In case one, where the database was the slowest component, the trading engines appeared to be I/O bound, waiting for the results of the database inserts. Since Node.js uses the non-blocking paradigm for all its code, it performed better than the Java solution. While I used Tomcat 8 with its preconfigured non-blocking (NIO) connector, the MySQL driver was the standard JDBC blocking version. In case two, where the database was faster, the trading engines were CPU bound, and the Java solution worked out faster.

My results were not actually that surprising - Node.js is well known to perform well, especially under blocking conditions. See the following two links for results which I think correlate well with my results: What Makes Node.js Faster Than Java? and Analysis of PayPal's Node-vs-Java Benchmarks. The comments at the end of the second link are very interesting and I feel mostly valid points.

Something I didn't try was to optimise the Java solution by making the persistence also non-blocking, so that it too was an entirely non-blocking solution. It would be possible because a non-blocking (albeit non-JDBC) MySQL driver exists. But it would also require changing the design of the Java solution. And, as pointed out in one of the comments in the above links, perhaps this redesign would be the most challenging part to the average Java programmer, who until recently, if at all, has never had to program within the asynchronous non-blocking paradigm. It isn't that it is hard, it's that it is different, and I suspect that following the recent success of Node.js, more and more asynchronous Java libraries will start to appear. Please note that this last paragraph is not meant so spawn any kind of debate - I am in no way saying that any one of Java, JavaScript, the JVM or Node.js is better. What I am saying is that a) I used to be a staunch supporter of Java and its ecosystem and in the last few years I have matured to realise that other platforms are also great and b) choose the right tools for the job at hand by evaluating with a proof of concept, for example as I have done here.

[1] Please note that the code provided in this article is not fit for any purpose and is certainly not production ready, nor representative of what I might produce professionally - it's hacked together to investigate the topics discussed above!

Copyright © 2014, Ant Kutschera
Social Bookmarks :  Add this post to Slashdot    Add this post to Digg    Add this post to Reddit    Add this post to Delicious    Add this post to Stumble it    Add this post to Google    Add this post to Technorati    Add this post to Bloglines    Add this post to Facebook    Add this post to Furl    Add this post to Windows Live    Add this post to Yahoo!