Being a member of the technical track of a large integrated development team occasionally has it benefits. In the smaller teams I am usually involved with it is often the case developers are forced into a continuous cycle of rush, rush, rush, deliver. Move onto the next project. The original developers responsible for a deliverable are often not there when it comes to pushing the product out to live. Thankfully that is not the case in my current employer. Recently though one of the teams in our department requested my help to track down an issue which was causing their web service to perform very poorly in the QA environment. Initially they thought it to be an issue with the network connection between the client (attached to our main web application) and the new machine which their service was hosted on. A couple of WGETs from the client server proved however that this was probably a problem the their client implementation.
We quickly bashed out a performance testing application by raiding and simplifying our production code for the most commonly used http clients. Since we have about 10 web services associated with our mainline web application we were able to find a decent spread of implementations. We yanked three handlers of particular interest:
- An Apache Commons client handler (commons-httpclient 3.1)
- A Jersey Client handler (jersey-core 1.11)
- A client making use of the Spring RestTemplate (springframework 3.0.5-RELEASE)
- We also created a four client using the java.net package from the SDK library. We used this as a control.
The objective of the experiment was to see how their teams handler stacked up against the our existing ones. Since the task had a fairly high priority at this stage we were assigned a veteran system administrator to deploy and test it asap. He pushed it out to machines with the following specifications:
- A: 1.4Ghz processors Sun SPARC, Solaris
- B: 2.85Ghz processors, Sun SPARC, Solaris
- C: 2.4Ghz processors, X86, Solaris
- D: 3.47Ghz, Linux
- E: 3.47Ghz, Linux (This machine was also the host of the web service we were performing GET requests on) The experiment immediately revealed that their handler (which used the Jersey Client) performed particularly badly when 100 sequential calls were made to the server. Here are the results:
Environment: Native, Jersey, Apache, Spring
A: 783, 29385, 3761, 1285
B: 505, 7625, 2130, 890
C: 506, 6563, 2000, 1153
D: 451, 3765, 870, 594
E: 469, 3812, 860, 520
Reviewing the teams Jersey calling code showed that they were misusing the Jersey client. Although the client looks like a lightweight object from its creation code (Client.create()), It is actually an expensive object to spawn (something noted in the Jersey java docs). We converted it to a Singleton and retested. It performed significantly better in all the environments:
Environment: Jersey Client (Singleton)
A: 2033
B: 1014
C: 1074
D: 735
E: 676
So another lesson learned the hard way. But at-least we caught it in our testing phase. Looking at the final results though - If you are in search for a speedy third party implementation (which should give you some additional bells and whistles) the Spring RestTemplate appears to have the fastest underlying implementation.
If you are interested in running the tests yourself I have pushed the test source to github: java-http-get-clients. Let me know if you get different results.