Sunday, August 11, 2013

setting up jersey client 2.0 to use httpclient, timeouts, and max connections

the goal

I've been trying to get my head wrapped around Jersey 2.0 client after playing around with the server a fair amount, having some experience configuring the client for Jersey 1.x. There's typically a standard set of things I look to configure:

  • Connection pooling via HttpClient
  • The read timeout
  • The connection timeout
  • Maximum number of connections
  • Maximum number of connections per host (or route)
  • JSON support

I decided to figure out how to accomplish this in Jersey 2.0, which was less obvious than I had anticipated. It's important to note that you absolutely should set these values up in your application, because the defaults are a combination of being overly restrictive and overly generous. The defaults when using HttpClient are as follows:

  • Infinite connection and read timeouts
  • By default when using Jersey's ApacheConnector you will get an instance of BasicClientConnectionManager which limits you to a single connection (though it is thread safe)
  • If you're configuring a PoolingClientConnectionManager instead, you'll have a maximum of 20 total connections with a maximum of 2 connections per host (or route)

As I'm sure you've already realized, these defaults are not going to scale at all.

for every parameter a config, and for every config a parameter

I've never been a fan of classes that contain a bunch of String based properties for an API because it can be difficult to figure out what goes where. There's no type to easily match against, so any method like setProperty(String key, Object value) could have anything set, and unless it's Javadoc'd that could be something from FooProperties.* or BarProperties.* for example.

(For the record, I like to define values like this with enum instances, sometimes with a common interface, to make it easier to find what should be used via an IDE)

I'm going to break down each part of the list above and how to go about configuring that feature by creating a class called ClientFactory one piece at a time.

connection and read timeouts

To begin, we need to start with a class called ClientConfig. Jersey uses this to configure client instances via its ClientBuilder API. We can set the connection and read timeouts with this class, as shown below:

using httpclient with connection pooling

Now let's set up a ClientConnectionManager that uses pooling. We should also set the limits on the number of connections a little bit higher, since 20 total and 2 per host is on the low side (we'll use 100 and 20 instead):

You have to use the implementation PoolingClientConnectionManager to set the max values; the interface ClientConnectionManager doesn't provide these methods. You can also be much more fine grained about how many connections per host are allowed with the setMaxPerRoute method. For example, let's say it was ok to have 40 max connections when hitting localhost:

configuring the jersey connector... with the config... which needs the connector

This code is a little obtuse, but it's how you need to have ClientConfig know about HttpClient and have HttpClient know about the timeout settings. We need to create a new ApacheConnector from the configuration, which specifies the pooled connection manager, and then set the connector in the configuration. Hopefully this makes more sense looking at the code:

constructing the client and adding json support

Now we have all the configuration we need to actually create the Client instance and set up JSON support:

putting it all together

Here's the full view of everything we set up to build the client. We're configuring the timeouts, setting up a pooled connection manager instance, restricting the maximum total number of connections and connections per host, adding an override for the maximum connections to localhost, and configuring JSON handling:

As a point of reference, here are the relevant pieces for a Maven pom to get the correct dependencies for this example:

14 comments:

  1. Thanks for the summary, this saved me a lot of time. One tip though: Rather use PoolingHttpClientConnectionManager what requires the org.apache.httpcomponents / httpclient dependency. The PoolingClientConnectionManager has been deprecated.

    ReplyDelete
  2. Okay, I'll take this back. Jersey does not support the newer HttpClientConnectionManager interface but requires the ClientConnectionManager interface. I guess my advice will be true at some point in the future.

    ReplyDelete
    Replies
    1. Yeah, one of my coworkers caught the same thing and told me about a week ago, then came back and let me know it's still leveraging the deprecated one. I think he may have filed a ticket to address it already.

      Thanks for the comments! Glad I was able to help somebody out!

      Delete
  3. Do you have snippet on how to do the connection pooling on jersey 1.x? We are still stuck on that version unfortunately.

    Thanks in advance

    ReplyDelete
    Replies
    1. I added a Gist that should get you close on a 1.x client instance. You can find it here: https://gist.github.com/theotherian/8312354

      In the interest of citing my sources, I also used this post as a reference: http://anosh-agiledeveloper.blogspot.com/2012/07/jersey-apachehttpclient-with-connection.html

      Delete
    2. Lastly, the example above is not a complete example of all the configuration options of a MultiThreadedHttpConnectionManager instance. You can set things like the max connections overall, stale connection checking, etc. You'll need to figure out what you or your organization's needs are in this case regarding SLAs and resource management.

      Delete
  4. I will give this a try.

    Thanks a lot! :)

    ReplyDelete
  5. This helped a lot. I am using Jersey 2.x. But, please advise if you have any Junit test cases for unit testing.That will really help to understand how this behaves in case of load

    ReplyDelete
  6. Could you please help me how to do the pooling in Jersey 1.x . Clientconfig don't have the connect method :(

    ReplyDelete
  7. I am tried using this on Wildfly 8.2 and I was getting error, any other setting needed for wildfly 8.2?

    ReplyDelete
  8. everytime we call the factory method create:
    We end up creating a new connection manager and a new client. Should not the connection manager be shared for all client instances?

    ReplyDelete
  9. It seems somehow my client doesn't timeout after the specified time. Do you know the reason?

    -Rajesh

    ReplyDelete
  10. Ia m not sure why, but is says "Cannot instantiate the type ClientConfig"

    ReplyDelete