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); } }
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.137.59