DeferredResult in Spring

In this post, we will explore the DeferredResult class in Spring. We can use the DeferredResult class in Spring MVC for asynchronous request processing.

 

Introduction

Spring MVC uses DeferredResult for asynchronous request processing to support and take advantage of Servlet 3.0. Servlet 3.0 introduced support for the asynchronous processing and same was introduced with Spring 3.2. There are a number of benefits of this technique.

  • It helps to unblock worker threads for long-running processes.
  • DeferredResult will offload the processing from the HTTP thread to a separate thread.
  • This helps in scaling the application during the peak load.

 

1. Maven Dependencies

We are using Spring Boot for this post. Spring Boot helps to quickly Bootstrap a Spring MVC application. Read our article Creating a Web Application with Spring Boot for the initial setup.

 

2. Traditional HTTP Call

@RestController
public class BlockingRESTController {

 @GetMapping("/blocking-request")
 public ResponseEntity < ? > blockHttpRequest() throws InterruptedException {

  Thread.sleep(4000);
  return ResponseEntity.ok("OK");
 }
}

With this traditional approach, our request thread is blocked until the request is processed. In above example, request thread will be blocked for 4000 milliseconds, for this duration, the thread will be in waiting for state and is not available to handle and server any new HTTP request.

This can lead to performance degradation if there are a number of long-running processes in the system. To handle these cases, the recommendation is to use non-blocking thread processing with the help of DeferredResult.

 

2. Non-Blocking Call using DeferredResult

Spring Provide DeferredResultoption to avoid thread blocking for the long-running processes.

@RestController
public class DeferredResultController {

 private static final Logger LOGGER = LoggerFactory.getLogger(DeferredResultController.class);

 @GetMapping("/asynchronous-request")
 public DeferredResult < ResponseEntity < ? >> asynchronousRequestProcessing(final Model model) {

  LOGGER.info("Started processing asynchronous request");
  final DeferredResult < ResponseEntity < ? >> deferredResult = new DeferredResult < > ();

  /**
   * Section to simulate slow running thread blocking process
   */
  ForkJoinPool forkJoinPool = new ForkJoinPool();
  forkJoinPool.submit(() -> {
   LOGGER.info("Processing request in new thread");
   try {
    Thread.sleep(4000);

   } catch (InterruptedException e) {
    LOGGER.error("InterruptedException while executing the thread {}", e.fillInStackTrace());
   }
   deferredResult.setResult(ResponseEntity.ok("OK"));
  });

  return deferredResult;
 }

}

In above example, the request is processed in a separate thread. This approach carries the following advantages.

  • Http worker threads released immediately by passing on the work to the non HTTP thread.
  • The worker threads are able to handle additional HTTP request without waiting for the long-running process to finish the work.

If we execute above controller, following output is visible in the server console.

2018-07-05 22:20:58.712  INFO 11879 --- [nio-8080-exec-1] c.j.d.DeferredResultController           : Started processing asynchronous request
2018-07-05 22:20:58.714  INFO 11879 --- [nio-8080-exec-1] c.j.d.DeferredResultController           : HTTP Wroker thread is relased.
2018-07-05 22:20:58.714  INFO 11879 --- [Pool-1-worker-1] c.j.d.DeferredResultController           : Processing request in new thread

Take a close look at the following output nio-8080-exec-1 and Pool-1-worker-1

 

3. Callback

The DeferredResult class provide the following three callbacks

  • onComletion – block on execute when the request completes.
  • onError – Execute code on error.
  • onTimeout – Custom code to invoke once timeout occurs 

 

3.1 onCompletion Callback

 deferredResult.onCompletion(() -> LOGGER.info("Processing complete"));

The above code block will execute on the successful execution of the request

 

3.1 onTimeout Callback

@GetMapping("/timeout-request")
public DeferredResult < ResponseEntity < ? >> onTimeoutExample(final Model model) {

 LOGGER.info("Started processing asynchronous request");
 final DeferredResult < ResponseEntity < ? >> deferredResult = new DeferredResult < ResponseEntity < ? >> (5000 l);
 deferredResult.onTimeout(() ->
  deferredResult.setErrorResult(
   ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT)
   .body("Request timeout.")));
 /**
  * Section to simulate slow running thread blocking process
  */
 ForkJoinPool forkJoinPool = new ForkJoinPool();
 forkJoinPool.submit(() -> {
  LOGGER.info("Processing request in new thread");
  try {
   Thread.sleep(6000);

  } catch (InterruptedException e) {
   LOGGER.error("InterruptedException while executing the thread {}", e.fillInStackTrace());
  }
  deferredResult.setResult(ResponseEntity.ok("OK"));
 });

 LOGGER.info("HTTP Wroker thread is relased.");
 return deferredResult;
}

In the above code, we are setting HttpStatus.REQUEST_TIMEOUT for the timeout. If you run above method, “Request timeout” sent to the REST client. In the similar fashion, we can also execute custom code for the onError() callback.

 

Summary

In this article, we explore the DeferredResult class in Spring. We covered the process to use the DeferredResult option in your code to scale your application. We discussed how to use DeferredResult in the creation of asynchronous endpoints. The complete source code is available over the GitHub.

2 thoughts on “DeferredResult in Spring”

Comments are closed.