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,就是指一次只能完成一件任务.如果有多个任务,就必须排队,前面一个 ...
随机推荐
- hdu 2072 单词数(字符串)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2072 题意 每行输入由小写字母和空格组成,统计每行中不同的单词数. 题解 题解一 比较简洁的解法,读入 ...
- AT1219 歴史の研究 回滚莫队
可在vj上提交:https://vjudge.net/problem/AtCoder-joisc2014_c 题意: IOI 国历史研究的第一人--JOI 教授,最近获得了一份被认为是古代 IOI 国 ...
- Common Divisors CodeForces - 1203C
题意: 给你n个数,让你找出来公因子有多少个.公因子:对于这n个数,都能被这个公因子整除 题解: 只需要找出来这n个数的最大公因子x,然后找出来有多少不同数能把x给整.(因为我们可以保证x可以把这n个 ...
- Cobalt Strike特征隐藏
前言 首先红蓝对抗的时候,如果未修改CS特征.容易被蓝队溯源. 前段时间360公布了cobalt strike stage uri的特征,并且紧接着nmap扫描插件也发布了.虽说这个特征很早就被发现了 ...
- Bing壁纸-20200417
- GO - 高级编程
https://books.studygolang.com/gopl-zh/ https://chai2010.cn/advanced-go-programming-book/
- TypeScript Version 23 Design Patterns
TypeScript Version 23 Design Patterns TypeScript 设计模式 https://refactoring.guru/design-patterns/types ...
- Redis in Action : Redis 实战学习笔记
1 1 1 Redis in Action : Redis 实战学习笔记 1 http://redis.io/ https://github.com/antirez/redis https://ww ...
- js console 性能测试 & don't-use-array-foreach-use-for-instead
don't-use-array-foreach-use-for-instead slower https://coderwall.com/p/kvzbpa/don-t-use-array-foreac ...
- Chart.js & CPU 性能监控
Chart.js 可视化动态 CPU 性能监控 https://github.com/gildata/RAIO/issues/337 https://github.com/chartjs/Chart. ...