SpringMVC异步处理的 5 种方式示例详解
前段时间研究了下diamond的原理,其中有个重要的知识点是长连接的实现,用到了servlet的异步处理。异步处理最大的好处是可以提高并发量,不阻塞当前线程。其实SpringMVC也支持了异步处理,本文记录下相关的技术点。
异步处理demo
如果要启用异步返回,需要开启@EnableAsync。如下的代码中,使用DeferredResult进行异步处理。
请求进来后,首先创建DeferredResult对象,设置超时时间为60秒。然后指定DeferredResult在异步完成和等待超时时的回调。同步的处理只需要创建异步任何,然后返回DeferredResult即可。这样SpringMVC处理完此次请求后,不会立即返回response给客户端,会一直等待DeferredResult处理完成。如果DeferredResult没有在60秒内处理完成,就会触发超时,然后返回response给客户端。
@RequestMapping(value="/async/demo") publicDeferredResultasync(){ //创建DeferredResult,设置超时时间60s DeferredResult deferredResult=newDeferredResult<>((long)60*1000); Stringuuid=UUID.randomUUID().toString(); Runnablecallback=()->manager.remove(deferredResult,uuid); //设置完成和超时的回调 deferredResult.onCompletion(callback); deferredResult.onTimeout(callback); //创建异步任务 manager.addAsyncTask(deferredResult,uuid); //同步返回DeferredResult returndeferredResult; }
对于异步任务来说,需要持有DeferredResult对象。在异步处理结束时,需要手动调用DeferredResult.setResult完成输出。调用setResult时,数据输出写到客户端,然后触发异步完成事件执行回调。
task.getDeferredResult().setResult(ConfigJsonUtils.toJsonString(map));
使用DeferredResult进行异步处理
DeferredResult这个类代表延迟结果。DeferredResult可以用在异步任务中,其他线程能够获取DeferredResult并设置DeferredResult的返回数据。通常可以使用线程池、队列等配合DeferredResult实现异步处理。
根据官方描述,SpringMVC处理流程如下:
- 把controller返回的DeferredResult保存在内存队列或集合当中;
- SpringMVC调用request.startAsync(),开启异步;
- DispatcherServlet和所有的Filter退出当前请求线程;
- 业务应用在异步线程中设置DeferredResult的返回值,SpringMVC会再次发送请求;
- DispatcherServlet再次被调用,并使用DeferredResult的返回值;
使用Callable进行异步处理
使用Callable进行异步处理与DeferredResult类似。不同的是,Callable会交给系统指定的TaskExecutor执行。
根据官方描述,SpringMVC处理流程如下:
- controller返回Callable;
- SpringMVC调用request.startAsync(),开启异步,提交Callable到一个任务线程池;
- DispatcherServlet和所有的Filter退出当前请求线程;
- 业务应用在异步线程中返回值,SpringMVC会再次发送请求;
- DispatcherServlet再次被调用,并使用Callable的返回值;
@RequestMapping(value="/async/demo") publicCallableasync(){ Callable callable=()->String.valueOf(System.currentTimeMillis()); //同步返回 returncallable; }
使用ListenableFuture进行异步处理
ListenableFuture作为返回值,与DeferredResult类似。也需要使用者自行处理异步线程,但不支持超时、完成回调,需要自行处理。
@RequestMapping(value="/async/demo") publicListenableFutureasync(){ ListenableFutureTask ListenableFuture=newListenableFutureTask<>(()->{ returnString.valueOf(System.currentTimeMillis()); }); Executors.newSingleThreadExecutor().submit(ListenableFuture); returnListenableFuture; }
使用ResponseBodyEmitter进行异步处理
DeferredResult和Callable都只能返回一个异步值。如果需要返回多个对象,就要使用ResponseBodyEmitter。返回的每个对象都会被HttpMessageConverter处理并写回输出流。如果希望设置更多返回数据,如header、status等,可以把ResponseBodyEmitter作为ResponseEntity的实体数据返回。
@RequestMapping("/async/responseBodyEmitter") publicResponseBodyEmitterresponseBodyEmitter(){ ResponseBodyEmitterresponseBodyEmitter=newResponseBodyEmitter(); Executors.newSingleThreadExecutor().submit(()->{ try{ responseBodyEmitter.send("demo"); responseBodyEmitter.send("test"); responseBodyEmitter.complete(); }catch(Exceptionignore){} }); returnresponseBodyEmitter; }
使用StreamingResponseBody进行异步处理
如果希望跳过返回值的自动转换,直接把输出流写入OutputStream,可以使用StreamingResponseBody。也可以作为ResponseEntity的实体数据返回。
@RequestMapping("/async/streamingResponseBody") publicStreamingResponseBodystreamingResponseBody(){ StreamingResponseBodystreamingResponseBody=outputStream->{ Executors.newSingleThreadExecutor().submit(()->{ try{ outputStream.write("streamingResponseBody".getBytes()); }catch(IOExceptionignore){} }); }; returnstreamingResponseBody; }
各种处理方式的对比
|
数据转换 |
回调 |
线程池 |
|
DeferredResult |
1次 |
有 |
完成、超时 |
自行处理 |
Callable |
1次 |
有 |
无 |
系统处理 |
ListenableFuture |
1次 |
有 |
无 |
自行处理 |
ResponseBodyEmitter |
多次 |
有 |
无 |
自行处理 |
StreamingResponseBody |
多次 |
无 |
无 |
自行处理 |
到此这篇关于SpringMVC异步处理的5种方式的文章就介绍到这了,更多相关SpringMVC异步处理内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。