`

Spring mvc callable 异步处理过程Asynchronous Request Processing

 
阅读更多

Spring MVC 3.2 introduced Servlet 3 based asynchronous request processing. Instead of returning a value, as usual, a controller method can now return ajava.util.concurrent.Callable and produce the return value from a Spring MVC managed thread. Meanwhile the main Servlet container thread is exited and released and allowed to process other requests. Spring MVC invokes the Callable in a separate thread with the help of a TaskExecutor and when the Callablereturns, the request is dispatched back to the Servlet container to resume processing using the value returned by the Callable. Here is an example of such a controller method:

@RequestMapping(method=RequestMethod.POST)
public Callable<String> processUpload(final MultipartFile file) {

    return new Callable<String>() {
        public String call() throws Exception {
            // ...
            return "someView";
        }
    };

}

Another option is for the controller method to return an instance of DeferredResult. In this case the return value will also be produced from any thread, i.e. one that is not managed by Spring MVC. For example the result may be produced in response to some external event such as a JMS message, a scheduled task, and so on. Here is an example of such a controller method:

@RequestMapping("/quotes")
@ResponseBody
public DeferredResult<String> quotes() {
    DeferredResult<String> deferredResult = new DeferredResult<String>();
    // Save the deferredResult somewhere..
    return deferredResult;
}

// In some other thread...
deferredResult.setResult(data);

This may be difficult to understand without any knowledge of the Servlet 3.0 asynchronous request processing features. It would certainly help to read up on that. Here are a few basic facts about the underlying mechanism:

  • ServletRequest can be put in asynchronous mode by calling request.startAsync(). The main effect of doing so is that the Servlet, as well as any Filters, can exit but the response will remain open to allow processing to complete later.
  • The call to request.startAsync() returns AsyncContext which can be used for further control over async processing. For example it provides the methoddispatch, that is similar to a forward from the Servlet API except it allows an application to resume request processing on a Servlet container thread.
  • The ServletRequest provides access to the current DispatcherType that can be used to distinguish between processing the initial request, an async dispatch, a forward, and other dispatcher types.

With the above in mind, the following is the sequence of events for async request processing with a Callable:

  • Controller returns a Callable.
  • Spring MVC starts asynchronous processing and submits the Callable to a TaskExecutor for processing in a separate thread.
  • The DispatcherServlet and all Filter’s exit the Servlet container thread but the response remains open.
  • The Callable produces a result and Spring MVC dispatches the request back to the Servlet container to resume processing.
  • The DispatcherServlet is invoked again and processing resumes with the asynchronously produced result from the Callable.

The sequence for DeferredResult is very similar except it’s up to the application to produce the asynchronous result from any thread:

  • Controller returns a DeferredResult and saves it in some in-memory queue or list where it can be accessed.
  • Spring MVC starts async processing.
  • The DispatcherServlet and all configured Filter’s exit the request processing thread but the response remains open.
  • The application sets the DeferredResult from some thread and Spring MVC dispatches the request back to the Servlet container.
  • The DispatcherServlet is invoked again and processing resumes with the asynchronously produced result.

For further background on the motivation for async request processing and when or why to use it please read this blog post series.

Exception Handling for Async Requests

What happens if a Callable returned from a controller method raises an Exception while being executed? The short answer is the same as what happens when a controller method raises an exception. It goes through the regular exception handling mechanism. The longer explanation is that when a Callable raises an Exception Spring MVC dispatches to the Servlet container with the Exception as the result and that leads to resume request processing with the Exception instead of a controller method return value. When using a DeferredResult you have a choice whether to call setResult or setErrorResult with an Exception instance.

Intercepting Async Requests

HandlerInterceptor can also implement AsyncHandlerInterceptor in order to implement the afterConcurrentHandlingStarted callback, which is called instead of postHandle and afterCompletion when asynchronous processing starts.

HandlerInterceptor can also register a CallableProcessingInterceptor or a DeferredResultProcessingInterceptor in order to integrate more deeply with the lifecycle of an asynchronous request and for example handle a timeout event. See the Javadoc of AsyncHandlerInterceptor for more details.

The DeferredResult type also provides methods such as onTimeout(Runnable) and onCompletion(Runnable). See the Javadoc of DeferredResult for more details.

When using a Callable you can wrap it with an instance of WebAsyncTask which also provides registration methods for timeout and completion.

HTTP Streaming

A controller method can use DeferredResult and Callable to produce its return value asynchronously and that can be used to implement techniques such as long polling where the server can push an event to the client as soon as possible.

What if you wanted to push multiple events on a single HTTP response? This is a technique related to "Long Polling" that is known as "HTTP Streaming". Spring MVC makes this possible through the ResponseBodyEmitter return value type which can be used to send multiple Objects, instead of one as is normally the case with@ResponseBody, where each Object sent is written to the response with an HttpMessageConverter.

Here is an example of that:

@RequestMapping("/events")
public ResponseBodyEmitter handle() {
    ResponseBodyEmitter emitter = new ResponseBodyEmitter();
    // Save the emitter somewhere..
    return emitter;
}

// In some other thread
emitter.send("Hello once");

// and again later on
emitter.send("Hello again");

// and done at some point
emitter.complete();

Note that ResponseBodyEmitter can also be used as the body in a ResponseEntity in order to customize the status and headers of the response.

HTTP Streaming With Server-Sent Events

SseEmitter is a sub-class of ResponseBodyEmitter providing support for Server-Sent Events. Server-sent events is a just another variation on the same "HTTP Streaming" technique except events pushed from the server are formatted according to the W3C Server-Sent Events specification.

Server-Sent Events can be used for their intended purpose, that is to push events from the server to clients. It is quite easy to do in Spring MVC and requires simply returning a value of type SseEmitter.

Note however that Internet Explorer does not support Server-Sent Events and that for more advanced web application messaging scenarios such as online games, collaboration, financial applicatinos, and others it’s better to consider Spring’s WebSocket support that includes SockJS-style WebSocket emulation falling back to a very wide range of browsers (including Internet Explorer) and also higher-level messaging patterns for interacting with clients through a publish-subscribe model within a more messaging-centric architecture. For further background on this see the following blog post.

 

HTTP Streaming Directly To The OutputStream

ResponseBodyEmitter allows sending events by writing Objects to the response through an HttpMessageConverter. This is probably the most common case, for example when writing JSON data. However sometimes it is useful to bypass message conversion and write directly to the response OutputStream for example for a file download. This can be done with the help of the StreamingResponseBody return value type.

Here is an example of that:

@RequestMapping("/download")
public StreamingResponseBody handle() {
    return new StreamingResponseBody() {
        @Override
        public void writeTo(OutputStream outputStream) throws IOException {
            // write...
        }
    };
}

Note that StreamingResponseBody can also be used as the body in a ResponseEntity in order to customize the status and headers of the response.

分享到:
评论

相关推荐

    看透springMvc源代码分析与实践

    22.1.1 Servlet 3.0处理异步请求实例282 22.1.2 异步请求监听器Async-Listener284 22.2 Spring MVC中的异步请求286 22.2.1 Spring MVC中异步请求相关组件286 22.2.2 Spring MVC对异步请求的支持297 22.2.3 ...

    CPLEX Callable Library 2009 manual

    CPLEX Callable Library 2009 manual

    java callable(多线程)

    callable用法

    Spring注解驱动开发实战-springMVC

    ServletContext注册三大组件 springmvc-定制与接管SpringMVC springmvc-异步请求-返回Callable 异步请求-返回DeferredResult.

    JAVA中Callable的使用

    JAVA中Callable的使用。下载后评论可返分

    Callable,Future的使用方式

    Callable,Future的使用方式,里面使用了三种使用方式分别是FutureTask,ExecutorService,ExecutorCompletionService

    callable_traits, callable类型的现代 C 型特征和泛函.zip

    callable_traits, callable类型的现代 C 型特征和泛函 Boost.CallableTraits CallableTraits是一个C 11头库,用于检查。合成和分解可以调用类型。这里有最新的文档,这里是 。在CallableTraits中,被正式地检查过,...

    java并发包之Callable和Future

    java并发包之Callable和Future java并发包之Callable和Future java并发包之Callable和Future java并发包之Callable和Future java并发包之Callable和Future java并发包之Callable和Future

    Java使用Callable和Future创建线程操作示例

    主要介绍了Java使用Callable和Future创建线程操作,结合实例形式分析了java使用Callable接口和Future类创建线程的相关操作技巧与注意事项,需要的朋友可以参考下

    Mybatis-Callable.zip

    一直以来感觉使用Mybatis查询存储过程很蹩脚,不知道有没有人和我感觉一样。写了一个拦截器,可以把储存过程的输出参数当做返回值来返回。其实也简单不了多少,只不过在设计Dao接口的时候能更统一吧。 使用方式...

    总结了Thread-Callable-Future的小demo

    自留demo,主要是Thread-Callable-Future的基本实现。 参考文章: 1、深入理解Callable https://blog.csdn.net/qq_19431333/article/details/77483763 2、彻底理解Java的Future模式: ...

    smart-doc是一款同时支持JAVA REST API和Apache Dubbo RPC接口文档生成的工具,.rar

    支持Spring MVC、Spring Boot、Spring Boot Web Flux(controller书写方式)、Feign。 支持Callable、Future、CompletableFuture等异步接口返回的推导。 支持JavaBean上的JSR303参数校验规范,包括分组验证。 对JSON...

    Callable, Future, FutureTask2

    通过合规策略对服务器进行监控,确保服务器的运行、帐号在服务器上的操作符合预设的规则。日志:收集、整理服务器的日志信息,提供给管理员查看,并作为异常判断、故障排查的依据。进程:监控服务器上的进程,并对...

    Callable和Future.doc

    Callable和Future详解: Callable和Runnable有几点不同: (1)Callable规定的方法是call(),而Runnable规定的方法是run();...(4)Callable返回结果可以被Future拿到,即Future可以拿到异步执行任务的返回值。

    Django错误:TypeError at / 'bool' object is not callable解决

    主要介绍了Django 错误:TypeError at / 'bool' object is not callable解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    115-springboot-demo-smart-doc.rar

    支持Spring MVC、Spring Boot、Spring Boot Web Flux(controller书写方式)、Feign。支持Callable、Future、CompletableFuture等异步接口返回的推导。支持JavaBean上的JSR303参数校验规范,包括分组验证。对JSON请求...

    Java中的Callable接口最全讲义

    目录: 简介 Callable接口的基本用法 2.1 创建Callable任务 2.2 提交Callable任务 2.3 获取任务执行结果 Future接口的使用 3.1 获取任务执行状态 ...Callable的异常处理 Callable的应用场景 最佳实践与注意事项

    使用Runnable模拟Callable接口实现线程有返回值

    大家都知道Runnable和Callable接口都可以作为其他线程执行的任务,但是Runnable接口的run方法没有返回值,而Callable接口的call方法有返回值,那么Callable接口是如何做到的呢?在此我给出一个Demo,看看通过...

    Java多线程Callable接口

    Runnable是执行工作的独立任务,但是它不返回任何值,如果你希望任务在完成时能够返回一个值,那么可以实现Callable接口而不是Runnable接口。在Java SE5中引入的Callable是一种具有类型参数的泛型,它的类型参数表示...

    Java中的Runnable,Callable,Future,FutureTask的比较

    主要介绍了Java中的Runnable,Callable,Future,FutureTask的比较的相关资料,需要的朋友可以参考下

Global site tag (gtag.js) - Google Analytics