I've a long-running task defined in a Spring service. It is started by a Spring MVC controller. I want to start the service and return back an HttpResponse
to the caller before the service ends. The service saves a file on file system at end. In javascript I've created a polling job to check service status.
In Spring 3.2 I've found the @Async
annotation, but I don't understand how it is different from DeferredResult
and Callable
. When do I have to use @Async
and when should I use DeferredResult
?
DeferredResult, available from Spring 3.2 onwards, assists in offloading a long-running computation from an http-worker thread to a separate thread. Although the other thread will take some resources for computation, the worker threads are not blocked in the meantime and can handle incoming client requests.
The @EnableAsync annotation switches on Spring's ability to run @Async methods in a background thread pool. This class also customizes the Executor by defining a new bean. Here, the method is named taskExecutor , since this is the specific method name for which Spring searches.
Also callable is an alternative for Runnable, in the sense, It can return results and throw checked exceptions. Say you have a method public String aMethod(){ } This can be made asynchronous by simply returning a Callable interface. public Callable<String> aMethod(){ }
The main difference between the two frameworks is that spring-mvc is based on thread pools, while spring-webflux is based on event-loop mechanism. Both the models support commonly used annotations such as @Controller . A developer can run a reactive client from a spring-mvc controller to make calls to remote services.
Async annotates a method so it is going to be called asynchronously.
@org.springframework.stereotype.Service public class MyService { @org.springframework.scheduling.annotation.Async void DoSomeWork(String url) { [...] } }
So Spring could do so you need to define how is going to be executed. For example:
<task:annotation-driven /> <task:executor id="executor" pool-size="5-10" queue-capacity="100"/>
This way when you call service.DoSomeWork("parameter") the call is put into the queue of the executor to be called asynchronously. This is useful for tasks that could be executed concurrently.
You could use Async to execute any kind of asynchronous task. If what you want is calling a task periodically you could use @Scheduled (and use task:scheduler instead of task:executor). They are simplified ways of calling java Runnables.
DeferredResult<> is used to answer to a petition without blocking the Tomcat HTTP thread used to answer. Usually is going to be the return value for a ResponseBody annotated method.
@org.springframework.stereotype.Controller { private final java.util.concurrent.LinkedBlockingQueue<DeferredResult<String>> suspendedRequests = new java.util.concurrent.LinkedBlockingQueue<>(); @RequestMapping(value = "/getValue") @ResponseBody DeferredResult<String> getValue() { final DeferredResult<String> result = new DeferredResult<>(null, null); this.suspendedRequests.add(result); result.onCompletion(new Runnable() { @Override public void run() { suspendedRequests.remove(result); } }); service.setValue(result); // Sets the value! return result; } }
The previous example lacks one important thing and it's that doesn't show how the deferred result is going to be set. In some other method (probably the setValue method) there is going to be a result.setResult(value). After the call to setResult Spring is going to call the onCompletion procedure and return the answer to the HTTP request (see https://en.wikipedia.org/wiki/Push_technology#Long_polling).
But if you just are executing the setValue synchronously there is no advantage in using a deferred result.Here is where Async comes in hand. You could use an async method to set the return value in some point in the future using another thread.
@org.springframework.scheduling.annotation.Async void SetValue(DeferredResult<String> result) { String value; // Do some time consuming actions [...] result.setResult(value); }
Async is not needed to use a deferred result, its just one way of doing it.
In the example there is a queue of deferred results that, for example, a scheduled task could be monitoring to process it's pending requests. Also you could use some non blocking mechanism (see http://en.wikipedia.org/wiki/New_I/O) to set the returning value.
To complete the picture you could search information about java standard futures (http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/Future.html) and callables (http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/Callable.html) that are somewhat equivalent to Spring DeferredResult and Async.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With