Tuesday Jun 28, 2011

Jersey non blocking client

Although Jersey already have support for making asynchronous requests, it is implemented by standard blocking way - every asynchronous request is handled by one thread and that thread is released only after request is completely processed. That is OK for lots of cases, but imagine how that will work when you need to do lots of parallel requests. Of course you can limit (and its really wise thing to do, you do want control your resources) number of threads used for asynchronous requests, but you'll get another maybe not pleasant consequence - obviously processing time will increase.

There are few projects which are trying to deal with that problem, commonly named as async http clients. I didn't want to "re-implement a wheel" and I decided I'll use AHC - Async Http Client made by Jeanfrancois Arcand. There is also interesting implementation from Apache - HttpAsyncClient, but it is still in "very early stages of development" and others haven't been in similar or better shape as AHC.

How this works? Non-blocking clients allow users to make same asynchronous requests as we can do with standard approach but implementation is different - threads are better utilized, they don't spend most of time in idle state. Simply described - when you make a request (send it over the network), you are waiting for reply from other side. And there comes main advantage of non-blocking approach - it uses these threads for further work, like making other requests or processing responses etc.. Idle time is minimized and your resources (threads) will be far better used.

Who should consider using this? Everyone who is making lots of asynchronous requests. I haven't done proper benchmark yet, but some simple dumb tests are showing huge improvement in cases where lots of concurrent asynchronous requests are made in short period.

Last but not least - this module is still experimental, so if you don't like something or if you have ideas for improvements/any feedback, feel free to comment this blog post, send mail to users@jersey.java.net or contact me personally. All feedback is greatly appreciated!

maven dependency (will be present in java.net maven 2 repo by the end of the day):

link: http://download.java.net/maven/2/com/sun/jersey/experimental/jersey-non-blocking-client

<dependency>
 <groupId>com.sun.jersey.experimental</groupId>
 <artifactId>jersey-non-blocking-client</artifactId>
 <version>1.9-SNAPSHOT</version>
</dependency>

code snippet:

 ClientConfig cc = new DefaultNonBlockingClientConfig();
 cc.getProperties().put(NonBlockingClientConfig.PROPERTY_THREADPOOL_SIZE, 10); // default value, feel free to change
 Client c = NonBlockingClient.create(cc);

 AsyncWebResource awr = c.asyncResource("http://oracle.com");

 Future<ClientResponse> responseFuture = awr.get(ClientResponse.class);

 // or
 awr.get(new TypeListener<ClientResponse>(ClientResponse.class) {
     @Override
     public void onComplete(Future<ClientResponse> f) throws InterruptedException {
          ...
     }
 });

javadoc (temporary location, won't be updated): http://anise.cz/~paja/jersey-non-blocking-client/

Tuesday May 05, 2009

Jersey Client - Using ContainerListener

I would like to introduce a possibility to monitor connections made within jersey client api. Monitoring in this case means have knowledge about amount of transferred bytes.

This functionality is provided (as many others) as a client filter, concretely ConnectionListenerFilter. To create ConnectionListenerFilterinstance, you have to implement OnStartConnectionListener. It might look as follows:

class ListenerFactory implements OnStartConnectionListener {
     public ContainerListener onStart(ClientRequest cr) {
         return new Listener();
    }
}

Looks simple (and it is). This factory creates ContainerListener instance based on ClientRequest. Important note: ClientRequest instance here may be used only for getting values. Any attempt to change anything will produce UnsupportedOperationException.

Ok. Now we need ContainerListener implementation. This class is defined as:

public abstract class ContainerListener {

    public void onSent(long delta, long bytes) {};

    public void onReceiveStart(long totalBytes) {};

    public void onReceived(long delta, long bytes) {};

    public void onFinish() {};
}

Parameters should be easy to understand, delta is always difference to previous call, bytes is sum of sent/received bytes so far and totalBytes is value from http header (if present, otherwise is set to -1).

Every request has a simple "lifecycle":

1) Send request
2) Receive response

Method onSent is called during step one only when request has some entity. When request is sent and response has no entity, onFinish() is called and that's it. More interesting scenario is when response contains some entity - onReceiveStart is called after sending a request and onReceived during data receiving. Method onFinish is called after whole entity is read.

You will probably have some nice GUI progress bar but I'm going to do only plain text log for now to keep it simple.

class Listener extends ContainerListener {
     @Override
     public void onSent(long delta, long bytes) {
         System.out.println("onSent: delta: " + delta + " bytes: " + bytes);
     }

     @Override
     public void onReceiveStart(long totalBytes) {
         System.out.println("onReceiveStart: " + totalBytes);
     }

     @Override
     public void onReceived(long delta, long bytes) {
         System.out.println("onReceived: delta: " + delta + " bytes: " + bytes);
     }

     @Override
     public void onFinish() {
         System.out.println("onFinish");
     }
}

And we're almost done! Last thing to do is register our ListenerFactory as a client filter:

    Client c = Client.create();

    c.addFilter(new ConnectionListenerFilter(new ListenerFactory()));

and actually do a request:

    WebResource r = c.resource("http://download.java.net/maven/2/com/sun/jersey/samples/jersey-samples/1.1.0-ea/jersey-samples-1.1.0-ea-project.zip");
    r.get(String.class);

Output should look like this:

onReceiveStart: 615534
onReceived: delta: 3677 bytes: 3677
onReceived: delta: 2608 bytes: 6285
...
onReceived: delta: 2608 bytes: 613949
onReceived: delta: 1585 bytes: 615534
onFinish

You can experiment with some requests containing entity (so you will see that onSent method is called during sending).

For more information about project Jersey visit http://jersey.dev.java.net

About

Pavel Bucek

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today