作者:丁仪
来源: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 给客户端。
对于异步任务来说,需要持有 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 的返回值;
使用 ListenableFuture 进行异步处理
ListenableFuture 作为返回值,与 DeferredResult 类似。也需要使用者自行处理异步线程,但不支持超时、完成回调,需要自行处理。
使用 ResponseBodyEmitter 进行异步处理
DeferredResult 和 Callable 都只能返回一个异步值。如果需要返回多个对象,就要使用 ResponseBodyEmitter。返回的每个对象都会被 HttpMessageConverter 处理并写回输出流。如果希望设置更多返回数据,如 header、status 等,可以把 ResponseBodyEmitter 作为 ResponseEntity 的实体数据返回。
使用 StreamingResponseBody 进行异步处理
如果希望跳过返回值的自动转换,直接把输出流写入 OutputStream,可以使用 StreamingResponseBody。也可以作为 ResponseEntity 的实体数据返回。
各种处理方式的对比
以上几种异步处理方式各有差异,需要按需取舍。对比如下。
|
|
可返回次数 |
数据转换 |
回调 |
线程池 |
|
DeferredResult |
1 次 |
有 |
完成、超时 |
自行处理 |
|
Callable |
1 次 |
有 |
无 |
系统处理 |
|
ListenableFuture |
1 次 |
有 |
无 |
自行处理 |
|
ResponseBodyEmitter |
多次 |
有 |
无 |
自行处理 |
|
StreamingResponseBody |
多次 |
无 |
无 |
自行处理 |
推荐阅读