Asynchronous processing: from Servlet 3.0 to JAX RS 2.0

When a Web server accepts a request from a client over HTTP, it creates a thread that handles this and only this connection over its complete lifetime. Threads a usually organized in pools so that threads can be re-used rather than freshly created for every request. Threads are quite heavy in terms of memory footprint, so it could be wise to limit the number of threads kept in the thread pool. However, the drawback of any limitation in the number of threads might be that new incoming connections from clients are rejected since there is no thread free to process the request. With HTTP 1.1 it is possible to keep connections between clients and the Web server alive between subsequent requests. While this improves latency, it further worsens scalability: threads are not returned to the pool although nothing has to be processed in the meantime between two requests. The latter problem was addressed in Web servers by using Java’s NIO API that allows for implementing a strategy called thread-per-request. Now, it is not necessary to keep the thread assigned to the connection when there is no request. That’s how Web servers like Tomcat or Jetty work today.

The Java servlet specification version 3.0 introduced the concept of asynchronous processing already some years ago. To put it simply, it means that in case of a request a thread is only responsible to accept the request and put it into a processing queue. The Java method that handles the request terminates almost immediately. Look at the following code snippet of a Java servlet:

1
2
3
4
5
6
7
8
9
10
11
12
@WebServlet( value = "/asynctest", asyncSupported = true )
public class AsyncTest extends HttpServlet
{
    @Override
    protected void doGet( HttpServletRequest req, final HttpServletResponse res ) throws ServletException, IOException
    {
        final AsyncContext ctx = req.startAsync( );
 
        ctx.setTimeout( 3000 );
        ctx.addListener( new MyListener( ) );
        ctx.start( new Task( ctx ) );
    }

Class MyListener is just implementing the interface AsyncListener and does nothing in the first run. This interface provides callback methods that can be used to do some additional stuff before and after the task is executed, as well as in the case of errors and timeouts. Class Task implements Runnable:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Task implements Runnable
{
    private AsyncContext context;
 
    Task( AsyncContext context )
    {
        this.context = context;
    }
 
    public void run( )
    {
        try
        {
            // place long running code here
            context.getResponse( ).getWriter( ).write( "Hello World" );
        }
        catch ( Exception e )
        {
             log( "Problem processing task", e );
        }
 
        context.complete( );
    }
}

Let’s now go further to REST interfaces, for which there is a Java API (JAX RS, JSR 311) in version 2.0, which is still not final. In this version, asynchronous processing has been added as well. We will show some code using Jersey which is the reference implementation of JSR 311:

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
@Path( "/asyncrest" )
public class RestAsync
{
    public RestAsync( )
    {}
 
    @GET
    @Produces( MediaType.APPLICATION_JSON )
    @Consumes( MediaType.APPLICATION_JSON )
    @ManagedAsync
    public void async( @Suspended final AsyncResponse ar )
    {
        ar.setTimeout( 5, TimeUnit.SECONDS );
	ar.setTimeoutHandler( new MyTimeoutHandler( ) );
	ar.resume( someWork( ) );
    }
 
    protected String someWork( )
    {
        try
	{
            // put some long running code here
	    return "Hello World ";
	}
	catch ( Exception e )
	{
	    e.printStackTrace( );
	}
    }

The TimeoutHandler is only necessary if you want to modify the default behavior in case of timeouts. When a timeout occurs, a HTTP response with status code 503 is returned, which means something like “server failure”. In one of my applications, I wanted to implemented some kind of HTTP Long Polling, for which a timeout is just the case of having no new information. Thus, I implemented a simple timeout handler that returns HTTP status code 200 in this case:

1
2
3
4
5
6
7
8
9
class MyTimeoutHandler implements TimeoutHandler
{
    @Override
    public void handleTimeout( AsyncResponse response )
    {
        Response r = Response.ok( ).status( HttpURLConnection.HTTP_OK ).build( );
        response.resume( r );
    }
}

In a next step, we will have a look at the new features with regard to REST clients that are provided with JAX RS 2.0.

Leave a Reply

Your email address will not be published. Required fields are marked *


*