2009-05-11

On the road to Camel 2.0 - Asynchronous routing

We have rewritten the Asynchronous routing in Camel 2.0.

The new Async API in Camel 2.0 leverages in much greater detail the Java Concurrency API and its support for executing tasks asynchronous. Therefore the Camel Async API should be familiar for users with knowledge of the Java Concurrency API.

Before we dig into this matter there are a few concepts we need to discuss as a background material we must understand before using the Async API. The concept is general for asynchronous messaging and will apply for other Integration Frameworks as such.

As a client we can submit a message as either:

The former is the postal service example where we send a postal mail and but a letter in a letterbox. We do not expect a reply in this conversation.

The latter is on the web where we using a web browser request to visit a web page and the remote web server replies with the web content.

The invocation and processing for both of them can happen either:
- synchronous
- asynchronous
From the client point of view, that is.

By using synchronous the client will wait until the conversation is over before continuing. With asynchronous the control to the client is returned immediately after the message have been submitted. The client can in the mean time do other work while the message is being routed and processed. At any given time in the future the client can decide to check whether the task is finished or not. In fact the client can wait until the task is complete and its result is returned to the client.

All together we have 2 x 2 combinations to understand. In this blog I will only show 1 of them. The Camel documentation contains all 4 of them with more details.

Asynchronous Request Reply
The asynchronous request reply is where the client sends a message to an Endpoint and the control is returned immediately back to the client. The message however is processed in another thread, the asynchronous thread. The client can continue doing other work and at the same time the asynchronous thread is processing the message. This is illustrated in the diagram below:










  
  1. The client sends an Async Request Reply message over Http to Camel. The control is immediately returned to the client application, that can continue and do other work while Camel routes the message.
  2. Camel invokes an external TCP service using synchronous Request Reply. The client application can do other work simultaneously.
  3. The client wants to get the reply so it uses the Future handle it got as response from step 1. With this handle it retrieves the reply, wait if nessasary if the reply is not ready.
Asynchronous Request Reply Example
Suppose we want to call a Http service but it is usually slow and thus we do not want to block and wait for the response, as we can do other important computation. So we can initiate an Async exchange to the Http endpoint and then do other stuff while the slow Http service is processing our request. And then a bit later we can use the Future handle to get the response from the Http service. 

First we define some routes in Camel. One for the Http service where we simulate a slow server as it takes at least 1 second to reply. And then other route that we want to invoke while the Http service is on route. This allows you to be able to process the two routes simultaneously:

// The mocks are here for unit test

// Some other service to return a name
// this is invoked synhronously
from("direct:name")
    .transform(constant("Claus")).to("mock:result");

// Simulate a slow http service (delaying 1 sec)
// we want to invoke async
from("jetty:http://0.0.0.0:9080/myservice")
    .delay(1000)
    .transform(constant("Bye World"))
    .to("mock:result");

And then we have the client API where we call the two routes and we can get the responses from both of them. As the code is based on unit test there is a bit of mock in there as well:

MockEndpoint mock = getMockEndpoint("mock:result");
// We expect the name job to be faster than the async
// job even though the async job was started first
mock.expectedBodiesReceived("Claus", "Bye World");

// Send a async request/reply message to the http endpoint
Future future = template.asyncRequestBody("http://0.0.0.0:9080/myservice",
                          "Hello World");

// We got the future so in the meantime we can do other stuff
// as this is Camel so lets invoke another request/reply
// route but this time is synchronous
String name = template.requestBody("direct:name",
                  "Give me a name", String.class);
assertEquals("Claus", name);

// Okay we got a name and we have done some other
// work at the same time the async route is running,
// but now its about time to wait and get get the response
// from the async task

// We use the extract future body to get the response
// from the future (waiting if needed) and then return
// a string body response. This allows us to do this in a
// single code line instead of using the JDK Future API
// to get hold of it, but you can also use that if you want
String response = template.extractFutureBody(future, String.class);
assertEquals("Bye World", response);

assertMockEndpointsSatisfied();

Summary
Its now much easier to do asynchronous messaging with Apache Camel. The Client API has be rewritten so it resembles and uses the Java Concurrency API. The Camel documentation contains much more information and also about the new async DSL that can be used within the route itself to turn a route into asynchronous.

2 comments:

jerdavis said...

Is it possible to bind the 4 async/sync routing types to an Interface using Spring Remoting?

Something like:

public String syncRequestReply( String arg );

public Future asyncRequestReply( String arg );

public void syncRequest( String arg );

public void asyncRequest( String arg );

Ahmed Theba said...

Would it be wise to use async processing if Apache Camel is running within an application server?

Apache camel has a camel-jboss component but where are the threads being created against? The jvm or the EE container?