SpringMVC异步处理的 5 种方式
作者:丁仪
来源:https://chengxuzhixin.com/blog/post/SpringMVC-yi-bu-chu-li-de-5-zhong-fang-shi.html
前段时间研究了下 diamond 的原理,其中有个重要的知识点是长连接的实现,用到了 servlet 的异步处理。异步处理最大的好处是可以提高并发量,不阻塞当前线程。其实 Spring MVC 也支持了异步处理,本文记录下相关的技术点。
异步处理 demo
如果要启用异步返回,需要开启 @EnableAsync。如下的代码中,使用 DeferredResult 进行异步处理。
请求进来后,首先创建 DeferredResult 对象,设置超时时间为 60 秒。然后指定 DeferredResult 在异步完成和等待超时时的回调。同步的处理只需要创建异步任何,然后返回 DeferredResult 即可。这样 Spring MVC 处理完此次请求后,不会立即返回 response 给客户端,会一直等待 DeferredResult 处理完成。如果 DeferredResult 没有在 60 秒内处理完成,就会触发超时,然后返回 response 给客户端。
@RequestMapping(value = "/async/demo")
public DeferredResult<String> async(){
// 创建 DeferredResult,设置超时时间 60s
DeferredResult<String> deferredResult = new DeferredResult<>((long)60 * 1000); String uuid = UUID.randomUUID().toString();
Runnable callback = () -> manager.remove(deferredResult, uuid);
// 设置完成和超时的回调
deferredResult.onCompletion(callback);
deferredResult.onTimeout(callback); // 创建异步任务
manager.addAsyncTask(deferredResult, uuid); // 同步返回 DeferredResult
return deferredResult;
}
对于异步任务来说,需要持有 DeferredResult 对象。在异步处理结束时,需要手动调用 DeferredResult.setResult 完成输出。调用 setResult 时,数据输出写到客户端,然后触发异步完成事件执行回调。
task.getDeferredResult().setResult(ConfigJsonUtils.toJsonString(map));
使用 DeferredResult 进行异步处理
DeferredResult 这个类代表延迟结果。DeferredResult 可以用在异步任务中,其他线程能够获取 DeferredResult 并设置 DeferredResult 的返回数据。通常可以使用线程池、队列等配合 DeferredResult 实现异步处理。
根据官方描述,Spring MVC 处理流程如下:
- 把 controller 返回的 DeferredResult 保存在内存队列或集合当中;
- Spring MVC 调用 request.startAsync(),开启异步;
- DispatcherServlet 和所有的 Filter 退出当前请求线程;
- 业务应用在异步线程中设置 DeferredResult 的返回值,Spring MVC 会再次发送请求;
- DispatcherServlet 再次被调用,并使用 DeferredResult 的返回值;
使用 Callable 进行异步处理
使用 Callable 进行异步处理与 DeferredResult 类似。不同的是,Callable 会交给系统指定的 TaskExecutor 执行。
根据官方描述,Spring MVC 处理流程如下:
- controller 返回 Callable;
- Spring MVC 调用 request.startAsync(),开启异步,提交 Callable 到一个任务线程池;
- DispatcherServlet 和所有的 Filter 退出当前请求线程;
- 业务应用在异步线程中返回值,Spring MVC 会再次发送请求;
- DispatcherServlet 再次被调用,并使用 Callable 的返回值;
@RequestMapping(value = "/async/demo")
public Callable<String> async(){
Callable<String> callable = () -> String.valueOf(System.currentTimeMillis());
// 同步返回
return callable;
}
使用 ListenableFuture 进行异步处理
ListenableFuture 作为返回值,与 DeferredResult 类似。也需要使用者自行处理异步线程,但不支持超时、完成回调,需要自行处理。
@RequestMapping(value = "/async/demo")
public ListenableFuture<String> async(){
ListenableFutureTask<String> ListenableFuture= new ListenableFutureTask<>(() -> {
return String.valueOf(System.currentTimeMillis());
});
Executors.newSingleThreadExecutor().submit(ListenableFuture);
return ListenableFuture;
}
使用 ResponseBodyEmitter 进行异步处理
DeferredResult 和 Callable 都只能返回一个异步值。如果需要返回多个对象,就要使用 ResponseBodyEmitter。返回的每个对象都会被 HttpMessageConverter 处理并写回输出流。如果希望设置更多返回数据,如 header、status 等,可以把 ResponseBodyEmitter 作为 ResponseEntity 的实体数据返回。
@RequestMapping("/async/responseBodyEmitter")
public ResponseBodyEmitter responseBodyEmitter(){
ResponseBodyEmitter responseBodyEmitter=new ResponseBodyEmitter(); Executors.newSingleThreadExecutor().submit(() -> {
try {
responseBodyEmitter.send("demo");
responseBodyEmitter.send("test");
responseBodyEmitter.complete();
} catch (Exception ignore) {}
}); return responseBodyEmitter;
}
使用 StreamingResponseBody 进行异步处理
如果希望跳过返回值的自动转换,直接把输出流写入 OutputStream,可以使用 StreamingResponseBody。也可以作为 ResponseEntity 的实体数据返回。
@RequestMapping("/async/streamingResponseBody")
public StreamingResponseBody streamingResponseBody(){
StreamingResponseBody streamingResponseBody = outputStream -> {
Executors.newSingleThreadExecutor().submit(() -> {
try {
outputStream.write("<html>streamingResponseBody</html>".getBytes());
} catch (IOException ignore) {}
});
};
return streamingResponseBody;
}
各种处理方式的对比
以上几种异步处理方式各有差异,需要按需取舍。对比如下。
可返回次数 |
数据转换 |
回调 |
线程池 |
|
DeferredResult |
1 次 |
有 |
完成、超时 |
自行处理 |
Callable |
1 次 |
有 |
无 |
系统处理 |
ListenableFuture |
1 次 |
有 |
无 |
自行处理 |
ResponseBodyEmitter |
多次 |
有 |
无 |
自行处理 |
StreamingResponseBody |
多次 |
无 |
无 |
自行处理 |
推荐阅读
SpringMVC异步处理的 5 种方式的更多相关文章
- SpringMVC 返回json的两种方式
前后台数据交互使用json是一种很重要的方式.本文主要探讨SpringMVC框架使用json传输的技术. 请注意,本文所提到的项目使用Spring 版本是4.1.7,其他版本在具体使用上可能有不一样的 ...
- springMVC返回数据的四种方式
转自:https://blog.csdn.net/itcats_cn/article/details/82119673 springMVC返回数据的四种方式:第一种,通过request.setAttr ...
- SpringMVC实现文件下载的两种方式及多文件下载
1.传统方法 @RequestMapping("/download") public String download( String fileName ,String filePa ...
- SpringMVC获取参数的几种方式
前言: 年末了,忙了一年了却发现系统的整理的东西很少,一些基础的东西都未做整理,这里就将它随便整理一下,增加一些印象,当然在网上看到一些好的资料也会整理下来以备后用.今天整理一下springMVC获取 ...
- Java 异步编程的几种方式
前言 异步编程是让程序并发运行的一种手段.它允许多个事情同时发生,当程序调用需要长时间运行的方法时,它不会阻塞当前的执行流程,程序可以继续运行,当方法执行完成时通知给主线程根据需要获取其执行结果或者失 ...
- 【整理】Ajax异步实现的几种方式总结
AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术.GET ...
- 总结ASP.NET MVC视图页使用jQuery传递异步数据的几种方式
在ASP.NET MVC的视图页向控制器传递异步数据,可能是数组,JavaScript对象,json,表单数据,等等. 关于数据,JavaScript对象有时候和json长得一模一样,有么有? var ...
- springmvc配置AOP的两种方式
spingmvc配置AOP有两种方式,一种是利用注解的方式配置,另一种是XML配置实现. 应用注解的方式配置: 先在maven中引入AOP用到的依赖 <dependency> <gr ...
- JavaScript处理异步请求的几种方式(取异步函数返回值)
JavaScript处理异步的几种方式 Javascript语言的执行环境是"单线程"(single thread,就是指一次只能完成一件任务.如果有多个任务,就必须排队,前面一个 ...
随机推荐
- 2019牛客暑期多校训练营(第五场)H-subsequence 2 (拓扑排序+思维)
>传送门< 题意: 给你几组样例,给你两个字符a,b,一个长度len,一个长度为len的字符串str,str是字符串s的子串 str是s删掉除过a,b两字符剩下的子串,现在求s,多种情况输 ...
- Gym - 102861B 、Gym - 102861F、Gym 102861G、Gym 102861L、Gym 102861N、Gym 101968C、Gym 101968D
训练赛链接:https://vjudge.net/contest/410049#problem/D Gym - 102861B 题意: 在一个二维平面上,给你一个船,问你在这个二维平面上有没有船重叠. ...
- python爬虫笔记Day01
python爬虫笔记第一天 Requests库的安装 先在cmd中pip install requests 再打开Python IDM写入import requests 完成requests在.py文 ...
- Cron表达式在 定时执行专家 5.0 中的使用方式
在<定时执行专家 V5.0>程序内部使用了包含 6 位的 Cron表达式,第一个字段(second)没有使用.程序内部一直 second 位是 0.在 Cron表达式的界面上可以设置 5位 ...
- const,volatile,static,typdef,几个关键字辨析和理解
1.const类型修饰符 const它限定一个变量初始化后就不允许被改变的修饰符.使用const在一定程度上可以提高程序的安全性和可靠性.它即有预编译命令的优点也有预编译没有的优点.const修饰的变 ...
- Java | 在 Java 中执行动态表达式语句: 前中后缀、Ognl、SpEL、Groovy、Jexl3
在一些规则集或者工作流项目中,经常会遇到动态解析表达式并执行得出结果的功能. 规则引擎是一种嵌入在应用程序中的组件,它可以将业务规则从业务代码中剥离出来,使用预先定义好的语义规范来实现这些剥离出来的业 ...
- App icons generator
App icons generator https://appicon.co/ Drag or select an app icon image (1024x1024) to generate dif ...
- Pygame 游戏开发 All In One
Pygame 游戏开发 All In One Pygame Pygame is a library for digital arts, games, music, making, and a comm ...
- js closures all in one
js closures all in one setTimeout 闭包,log(i, arr[¡]) var, let, closures, IIFE "use strict"; ...
- ts 函数重载
class User { constructor(public readonly name: string, public readonly value: Function) {} } class D ...