In this post, we will explore the DeferredResult class in Spring. We can use the DeferredResult class in Spring MVC
for asynchronous request processing.
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.
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.
@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.
Spring Provide DeferredResult
option 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.
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
The DeferredResult class provide the following three callbacks
deferredResult.onCompletion(() -> LOGGER.info("Processing complete"));
The above code block will execute on the successful execution of the request
@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.
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.
Hello!! Welcome to the Java Development Journal. We love to share our knowledge with our readers and love to build a thriving community.
Custom Type Converter in Spring MVC
01 Feb, 2019Spring Bean Validation – JSR 303
01 Jan, 2019Spring @RequestParam Annotation
25 Dec, 2018Spring MVC Interview Questions with Answers
06 Nov, 2018Spring MVC @ModelAttribute Annotation
21 Aug, 2018Session Attributes in Spring MVC
Thank you Finally my API run asynchronously
Hello Rishabh,
I am happy that it helped!!