相关文章推荐
宽容的水煮肉  ·  试图使用钱包通过jdbc连接到oracle时 ...·  7 月前    · 
爱笑的小刀  ·  容器镜像服务_镜像构建_镜像授权_镜像托管-阿里云·  10 月前    · 
要出家的鞭炮  ·  mysql order by 慢 - CSDN文库·  10 月前    · 
打酱油的甘蔗  ·  调用DescribeAuditLogReco ...·  11 月前    · 
爱逃课的啤酒  ·  MySQL查看存储过程·  1 年前    · 
Code  ›  SpringMVC 教程 - 异步请求开发者社区
servlet springmvc配置 异步队列 springmvc框架
https://cloud.tencent.com/developer/article/1163307?areaSource=106002.8
正直的拐杖
2 年前
作者头像
代码拾遗
0 篇文章

SpringMVC 教程 - 异步请求

前往专栏
腾讯云
开发者社区
文档 意见反馈 控制台
首页
学习
活动
专区
工具
TVP
文章/答案/技术大牛
发布
首页
学习
活动
专区
工具
TVP
返回腾讯云官网
社区首页 > 专栏 > 代码拾遗 > 正文

SpringMVC 教程 - 异步请求

发布 于 2018-07-24 15:53:38
1.1K 0
举报

Spring MVC 集成了Servlet 3.0的异步请求处理:

  • controller 的方法返回 DeferredResult , Callable
  • controller 流式处理多个值,包括 SSE 和原生数据。
  • controller 使用reactive客户端,返回reactive 类型。
DeferredResult

在Servlet 容器 中启动异步支持之后,controller的方法可以通过 DeferredResult 包装返回值来支持异步处理。例如:

@GetMapping("/quotes")
@ResponseBody
public DeferredResult<String> quotes() {
    DeferredResult<String> deferredResult = new DeferredResult<String>();
    // Save the deferredResult somewhere..
    return deferredResult;
// From some other thread...
deferredResult.setResult(data);

controller可以通过其他线程异步返回结果,例如:响应时间(例如JMS消息),一个调度任务或者其他类型。

Callable

java.util.Callable 也可以包装任何需要异步支持的返回值。例如:

@PostMapping
public Callable<String> processUpload(final MultipartFile file) {
    return new Callable<String>() {
        public String call() throws Exception {
            // ...
            return "someView";
}

返回值由配置的 TaskExecutor 执行相应任务并返回结果。

处理异步请求
Servlet异步请求的处理过程如下:
  • 通过调用 request.startAsync() 开始异步处理。调用后Servlet,Filter等可以退出,但是响应开发,直到处理完成返回。
  • 调用 request.startAsync() 后返回 AsyncContext ,可以在后续的处理中使用AsyncContext获取各种信息。
  • ServletRequest 提供接口访问当前的 DispatcherType 以便区分初始请求,异步分配,重定向或者其他 DispatcherType 。
DeferredResult 处理过程:
  • controller返回一个 DeferredResult 并且将其保存到内存中的队列或者列表中。
  • Spring MVC调用 request.startAsync()
  • 同时 DispatcherServlet , Fileter ,退出请求处理线程,响应保持开放。
  • 应用从线程获取值设置 DeferredResult ,Spring MVC将请求发送回Servlet 容器。
  • 再次调用 DispatcherServlet ,获取异步返回值,恢复请求处理。
Callable 处理过程:
  • controller 返回一个 Callable
  • Spring MVC 调用 request.startAsync() ,将 Callable 提交到 TaskExecutor 处理
  • 同时 DispatcherServlet , Fileter ,退出请求处理线程,响应保持开放。
  • Callable 产生结果,Spring MVC将请求发送回Servlet 容器。
  • 再次调用 DispatcherServlet ,通过从 Callable 获取的返回值恢复请求处理。
异常处理

使用 DeferredResult 可以调用 setResult 或者 setErrorResult 来返回结果,调用这两个函数后Spring MVC都会将请求发送回Servlet 容器以完成处理。接着会检查时正常返回还是返回了异常,如果有异常返回就走一般的异常处理流程,例如:调用 @ExceptionHandler 方法。 使用 Callable 的处理流程大体相同,主要的区别是又Callable返回结果或者抛出异常。

拦截器

AsyncHandlerInterceptor 可以在异步请求处理开始后接收 afterConcurrentHandlingStarted 的回调代替 postHandle 和 afterCompltetion 。 CallableProcessingInterceptor 或者 DeferredResultProcessingInterceptor 深度继承异步处理请求的生命周期,例如超时处理等。 DeferredResult 提供了 onTimeout(Runnable) 和 onCompletion(Runnable) 的回调。详情可以查看JavaDoc。 Callable 可以取代 WebAsyncTask ,它提供了超时和完成的回调。

与WebFlux对比

Servlet API之前是为Filter-Servlet请求处理链构建的。在Servlet 3.0 添加了异步处理后,允许应用退出Filter-Servlet请求处理链,只保留响应开放以便日后处理。Spring MVC支持的异步处理就是建立在这项技术之上的。当controller返回一个 DeferredResult 后,Filter-Servlet处理链退出,Servlet容器的请求线程释放。稍后 DeferredResult 返回结果,开始一个异步调用,重新映射到controller但是并不在调用controller,使用 DeferredResult 的返回值继续处理结果。 作为对比Spring WebFlux既没有使用Servlet API也不需要这样的一个异步处理模型,因为它完全是异步设计的。异步处理内置在所有的WebFlux框架中,并且支持异步处理的每一个步骤。 从编程模型来看,Spring MVC和Spring WebFlux都支持异步处理和返回Reactive类型。Spring MVC甚至支持流处理。然而并不想WebFlxu一样使用非阻塞IO,每次写入响应无需单独的线程,SpringMVC单独写入响应仍然是阻塞的。 另一项区别就是Spring MVC不支持异步或者reactive类型作为函数参数。Spring WebFlux支持。

HTTP 流

DeferredResult 和 Callback 每次只能异步返回一个值。如果要返回到个值则可以用HTTP 流。

Objects

ResponseBodyEmitter 返回值可以讲多个对象生成一个流,每个对象都通过 HttpMessageConverter 序列化发送,例如:

@GetMapping("/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();

ResponseBodyEmitter 同样也可以放入 ResponseEntity ,这样就可以定制响应的header和状态了。 emitter 抛出 IOException 异常的时候(例如,远程client关闭),应用并不负责回收连接,也不会调用 emitter.complete() 或者 emitter.completeWithError 。相反,Servlet容器会自动初始化一个AsyncListener错误通知,Spring MVC将会调用 completeWithError ,反过来执行异步分配,应用继续执行正常的异常处理流程。

SSE

SseEmitter 是 ResponseBodyEmitter 的子类,提供了Server-Send Events的支持。例如:

@GetMapping(path="/events", produces=MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter handle() {
    SseEmitter emitter = new SseEmitter();
    // 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();

SSE 是将流发送到浏览器的主要方式,但是IE浏览器不支持。如果想要支持更多浏览器,可以使用Spring的SockJS。

原始数据

有时绕过消息转换,直接将流写入到响应的 OutputStream 更加实用,例如:下载。可以使用 StreamingResponseBody 作为返回值处理:

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

StreamingResponseBody 也可以放入 ResponseEntity 中来定制响应header和状态。

Reactive 类型

Spring MVC支持在controller使用reactive client 库。包括spring-webflux中的 WebClient 和Spring Data 中的reactive 数据资源库。在一些场景中,从controller返回reactive类型非常的方便。 Reactive返回处理方式如下:

  • 类似 DeferredResult 单一值的promise,例如:Reactor的Mono,RxJava的Single。
  • 类似 ResponseBodyEmitter 或者 SseEmitter 的多值流(multi-value stream),流的媒体类型是 application/stream+json 或者 text/event-stream 。例如,Reactor的Flux,RxJava的Observable。应用可以返回Flux
 
推荐文章
宽容的水煮肉  ·  试图使用钱包通过jdbc连接到oracle时出错“未能锁定”。-腾讯云开发者社区-腾讯云
7 月前
爱笑的小刀  ·  容器镜像服务_镜像构建_镜像授权_镜像托管-阿里云
10 月前
要出家的鞭炮  ·  mysql order by 慢 - CSDN文库
10 月前
打酱油的甘蔗  ·  调用DescribeAuditLogRecords查询SQL审计日志_云原生数据仓库AnalyticDB MySQL版(AnalyticDB for MySQL)-阿里云帮助中心
11 月前
爱逃课的啤酒  ·  MySQL查看存储过程
1 年前
今天看啥   ·   Py中国   ·   codingpro   ·   小百科   ·   link之家   ·   卧龙AI搜索
删除内容请联系邮箱 2879853325@qq.com
Code - 代码工具平台
© 2024 ~ 沪ICP备11025650号