Spring MVC的@ Async,DeferredResult和Callable之间的区别

Pre*_*ier 24 asynchronous spring-mvc callable deferred

我在Spring服务中定义了一个长期运行的任务.它由Spring MVC控制器启动.我想HttpResponse在服务结束之前启动服务并返回给调用者.该服务最后将文件保存在文件系统上.在javascript中,我创建了一个轮询工作来检查服务状态.

在Spring 3.2中我找到了@Async注释,但是我不明白它DeferredResult与它的区别Callable.我何时必须使用@Async,何时使用DeferredResult

Jos*_*ero 22

Async注释一个方法,因此它将被异步调用.

@org.springframework.stereotype.Service
public class MyService {
    @org.springframework.scheduling.annotation.Async
    void DoSomeWork(String url) {
        [...]
    }
}
Run Code Online (Sandbox Code Playgroud)

所以Spring可以这样做,你需要定义如何执行.例如:

<task:annotation-driven />
<task:executor id="executor" pool-size="5-10" queue-capacity="100"/>
Run Code Online (Sandbox Code Playgroud)

这样,当您调用service.DoSomeWork("parameter")时,调用将被放入执行程序的队列中,以便异步调用.这对于可以同时执行的任务很有用.

您可以使用Async来执行任何类型的异步任务.如果您想要定期调用任务,则可以使用@Scheduled(并使用task:scheduler而不是task:executor).它们是调用java Runnables的简化方法.

DeferredResult <>用于回答请求而不阻止用于回答的Tomcat HTTP线程.通常将成为ResponseBody注释方法的返回值.

@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

前面的示例缺少一个重要的事情,它没有显示如何设置延迟结果.在其他一些方法(可能是setValue方法)中,会有一个result.setResult(value).在调用setResult之后,Spring将调用onCompletion过程并返回HTTP请求的答案(请参阅https://en.wikipedia.org/wiki/Push_technology#Long_polling).

但是,如果您只是同步执行setValue,则使用延迟结果没有任何优势.这就是Async的用武之地.您可以使用异步方法在将来使用另一个线程设置返回值.

    @org.springframework.scheduling.annotation.Async
    void SetValue(DeferredResult<String> result) {
        String value;
        // Do some time consuming actions
        [...]
        result.setResult(value);
    }
Run Code Online (Sandbox Code Playgroud)

使用延迟结果不需要异步,它只是一种方法.

在该示例中,存在延迟结果队列,例如,可以监视计划任务以处理其待处理请求.您也可以使用一些非阻塞机制(请参阅http://en.wikipedia.org/wiki/New_I/O)来设置返回值.

要完成图片,您可以搜索有关Java标准期货(http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/Future.html)和callables(http://)的信息. docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/Callable.html)有点等同于Spring DeferredResult和Async.


Ker*_*ode 9

您的控制器最终是由servlet容器(我假设它是Tomcat)工作线程执行的功能。您的服务流以Tomcat开头,以Tomcat结束。Tomcat从客户端获取请求,保留连接,并最终向客户端返回响应。您的代码(控制器或servlet)在中间。

考虑以下流程:

  1. Tomcat获取客户端请求。
  2. Tomcat执行您的控制器。
  3. 释放Tomcat线程,但保持客户端连接(不返回响应),并在其他线程上运行繁重的处理。
  4. 完成繁重的处理后,使用其响应更新Tomcat并将其返回给客户端(通过Tomcat)。

因为Servlet(您的代码)和Servlet容器(Tomcat)是不同的实体,所以要允许此流程(释放tomcat线程,但保持客户端连接),我们需要在自己的contract(即Servletjavax.servlet中引入的包)中提供此支持。3.0。现在,回到您的问题,尽管控制器的返回值为DeferredResultor Callable,但是Spring MVC使用新的Servlet 3.0功能,尽管它们是两个不同的东西。Callable是的一部分的接口java.util,并且是对该Runnable接口的改进(应该由实例打算由线程执行的任何类实现)。Callable允许返回值,而Runnable不允许返回。DeferredResult是由Spring设计的,允许在Spring MVC中为异步请求处理提供更多选项(我将描述),并且该类仅保存结果(如其名称所隐含),而您的Callable实现则保存异步代码。因此,这意味着您可以在控制器中同时使用两者,使用来运行异步代码Callable并将结果设置为DeferredResult,这将是控制器的返回值。那么,将其DeferredResult用作返回值而不是Callable会得到什么呢?DeferredResult还内置了类似的回调onErroronTimeoutonCompletion。它使错误处理变得非常容易。此外,由于它只是结果容器,因此您可以选择任何线程(或线程池)在异步代码上运行。使用Callable,您没有此选择。

关于@Async,它要简单得多-使用注释Bean的方法@Async将使其在单独的线程中执行。默认情况下(可以覆盖),Spring使用a SimpleAsyncTaskExecutor实际异步运行这些方法。

总之,如果要在执行繁重的处理时释放Tomcat线程并保持与客户端的连接,则控制器应返回CallableDeferredResult。否则,您可以在带有注释的方法上运行代码@Async

  • 声称“Callable”是对“Runnable”的改进,就像说具有返回值的函数是对具有“void”返回类型的函数的改进。两者都有其可行的用例。 (4认同)

Bar*_*art 5

DeferredResult利用 Servlet 3.0 AsyncContext。当您需要返回结果时,它不会像其他线程那样阻塞线程。

另一个很大的好处是DeferredResult支持回调。