Asynchronous RESTful web services

 

Asynchronous RESTful web services

All the discussions on RESTful web services that we have had so far were based on the synchronous request and response model. When a client invokes a RESTful web API synchronously, the server keeps the request handling thread engaged till its request is processed. In this case, the entire request is processed in a single thread on the server. This model works if the request processing is finished quickly. The problem starts when a request takes more time to finish. In such a case, the thread used by the container to handle the long-running request will remain busy till the processing is over. This may adversely affect the scalability of the application when the load (concurrent requests) increases because the server will not have enough threads in the pool to handle the incoming requests. The asynchronous processing feature in JAX-RS solves this by processing the long-running requests asynchronously, as depicted in the following flow diagram. The client sends the request using AsyncInvoker to the target with a callback handler, thereby releasing the thread that is handling the current request back to the container. AsyncResponse responds later with response data once the processing is complete; this essentially increases the throughput of the server: 

The following example demonstrates how to use the asynchronous REST API in JAX-RS. This example exposes the findAllDepartments() resource class method as an asynchronous REST API. You can do this by injecting javax.ws.rs.container.AsyncResponse as a parameter to the desired resource method, as shown in the following code snippet:

@GET 
@Path("departments") 
@Produces(MediaType.APPLICATION_JSON) 
public void findAllDepartments(@Suspended AsyncResponse asyncResponse) 

The @Suspended annotation used for injecting AsyncResponse tells the JAX-RS runtime that this method is expected to be executed in asynchronous mode and the client connection should not be automatically closed by the runtime when the method returns. The asynchronous REST resource method can use the injected AsyncResponse instance to send the response back to the client by using another thread. The AsyncResponse instance that is injected to methods is bound to the running request and can be used to asynchronously provide the request processing result. The core operations available on this instance are as follows:

  • cancel(): This method cancels the request processing and passes the message to the suspended thread that was handling the current client request.
  • resume(): This method resumes the request processing and passes the message to the suspended thread that was handling the current client request.
  • register():This method is used for registering callbacks such as CompletionCallback, which is executed when the request finishes or fails, and ConnectionCallback, which is executed when a connection to a client is closed or lost.
  • setTimeout(): This method is used for specifying the timeout value for the asynchronous process. Upon timeout, the runtime will throw javax.ws.rs.ServiceUnavailableException, which will be translated into the 503 Service Unavailable HTTP status code and sent back to the caller. You can even plug in a custom timeout handler implementation (which implements javax.ws.rs.container.TimeoutHandler) by invoking the setTimeoutHandler(TimeoutHandler)method on the AsyncResponse object.

The following code snippet illustrates a complete asynchronous method implementation for your reference. This method reads the department's records from the database in a different thread. Upon successful retrieval of records, the result is set on the AsyncResponse instance and resumes the suspended request processing. The resumed request is processed in a new thread as a normal request. The framework executes all configured filters, interceptors, and exception mappers before sending the response back to the client.

//Other imports are omitted for brevity 
import javax.ws.rs.container.AsyncResponse; 
import javax.ws.rs.container.Suspended; 
import javax.ws.rs.container.TimeoutHandler; 
 
@Stateless 
@Path("hr/asynch") 
public class HRAsynchService {  
   private final ExecutorService executorService=Executors.newCachedThreadPool();

@GET @Path("departments") @Produces(MediaType.APPLICATION_JSON) public void findAllDepartments(@Suspended final AsyncResponse asyncResponse)
{ //Set time out for the request asyncResponse.setTimeout(10, TimeUnit.SECONDS);
Runnable longRunningDeptQuery = new Runnable(){ EntityManagerFactory emf =
Persistence.createEntityManagerFactory("HRPersistenceUnit");
EntityManager entityManagerLocal = emf.createEntityManager();
public void run() { CriteriaQuery cq =
entityManagerLocal.getCriteriaBuilder().createQuery(); cq.select(cq.from(Department.class)); List<Department> depts = entityManagerLocal.createQuery(cq).getResultList(); GenericEntity<List<Department>> entity = new GenericEntity<List<Department>>(depts) { }; asyncResponse.resume(Response.ok().entity(entity).build()); } }; executorService.execute(longRunningDeptQuery); } }

Asynchronous RESTful web service client

This section describes the usage of the asynchronous JAX-RS API on the client for calling the RESTful web APIs.

To invoke a REST API asynchronously on the client, you use javax.ws.rs.client.AsyncInvoker. The AsyncInvoker instance is obtained from the call of the Invocation.Builder.async() method, as shown in the following code:

//Other imports are omitted for brevity 
import javax.ws.rs.client.AsyncInvoker; 
import javax.ws.rs.client.Client; 
import javax.ws.rs.client.WebTarget; 
import javax.ws.rs.core.GenericType; 
import javax.ws.rs.core.Response; 
 
String BASE_URI =  
    "http://localhost:8080/hr-services/webresources";  
Client client = ClientBuilder.newClient(); 
WebTarget webTarget = 
client.target(BASE_URI).path("hr").path("departments"); AsyncInvoker asyncInvoker = webTarget.request(APPLICATION_JSON).async(); Future<Response> responseFuture = asyncInvoker.get(); Response response = responseFuture.get(); List<Department> depts = response.readEntity(new GenericType<List<Department>>() { }); response.close(); client.close();

The JAX-RS client-side API allows you to listen to events generated during the asynchronous invocation of the REST APIs by registering the callback handlers. To do this, you may need to implement javax.ws.rs.client.InvocationCallback and pass this instance as a parameter to the appropriate HTTP method call on the AsyncInvoker instance. The following code snippet illustrates how you can implement InvocationCallback for a GET method type. The InvocationCallback::completed() method is called upon the successful completion of a request. The InvocationCallback::failed() method is called upon the failure of a request for any reason:

//Other imports are omitted for brevity 
import javax.ws.rs.client.InvocationCallback; 
 
String BASE_URI =  
    "http://localhost:8080/hr-services/webresources"; 
 
final Client client = 
javax.ws.rs.client.ClientBuilder.newClient(); WebTarget webTarget = client.target(BASE_URI).path("hr").path("departments"); AsyncInvoker asyncInvoker = webTarget. request(javax.ws.rs.core.MediaType.APPLICATION_JSON).async(); Future<List<Department>> entity = asyncInvoker.get( new InvocationCallback<List<Department>>() { @Override public void completed(List<Department> response) { //Call back on request completion //You can process the result here, if required client.close(); } @Override public void failed(Throwable throwable) { //Call back on request failure //Handle the exception //log(...) method definition is not shown here log(throwable); } });


In synchronous web services, the client connection is accepted and processed in a single I/O thread by a server. In asynchronous web services, the container accepts the client connection in a thread and then dispatches the actual processing of the request to a different thread so that it can release the thread used for client connection. In the previous section, we saw the usage of AsyncInvoker.get(..) to asynchronously consume the RESTful service. In this case, although the calling request thread is released by the server, the client keeps waiting until the business logic execution is finished by the server calling the resumemethod of the injected AsyncResponse object. Behind the scenes, the underlying I/O thread from the client side continues to block until it gets a response from the server or a timeout occurs.


























































































Comments

Popular posts from this blog

Understanding the JAX-RS resource life cycle

Generating a chunked output using Jersey APIs