
DeferredResult异步处理spring mvc Demo
发布日期:2021-05-19 02:38:01
浏览次数:16
分类:博客文章
本文共 4697 字,大约阅读时间需要 15 分钟。
一、概述
spring mvc同步接口在请求处理过程中一直处于阻塞状态,而异步接口可以启用后台线程去处理耗时任务。简单来说适用场景:
1.高并发;2.高IO耗时操作。二、Demo
Spring MVC3.2之后支持异步请求,能够在controller中返回一个Callable或者DeferredResult。
1.Callable实例@Controllerpublic class CallableController { @RequestMapping(path = "/async1", method = RequestMethod.GET) @ResponseBody public CallableasyncRequest() { return () -> { final long currentThread = Thread.currentThread().getId(); final Date requestProcessingStarted = new Date(); Thread.sleep(6000L); final Date requestProcessingFinished = new Date(); return String.format( "request: [threadId: %s, started: %s - finished: %s]" , currentThread, requestProcessingStarted, requestProcessingFinished); }; }}
2.DeferredResult使用方式与Callable类似,但在返回结果上不一样,它返回的时候实际结果可能没有生成,实际的结果可能会在另外的线程里面设置到DeferredResult中去,能实现更加复杂的业务场景。
@Controllerpublic class DeferredResultController { private Map> deferredResultMap = new HashMap<>(); @ResponseBody @GetMapping("/get") public DeferredResult getId(@RequestParam Integer id) throws Exception { System.out.println("start hello"); DeferredResult deferredResult = new DeferredResult<>(); //先存起来,等待触发 deferredResultMap.put(id, deferredResult); System.out.println("end hello"); return deferredResult; } @ResponseBody @GetMapping("/set") public void setId(@RequestParam Integer id) throws Exception { // 让所有hold住的请求给与响应 if (deferredResultMap.containsKey(id)) { deferredResultMap.get(id).setResult("hello " + id); } }}
当从浏览器请求http://localhost:8080/get/1时,页面处于等待状态;当访问http://localhost:8080/set/1,前面的页面会返回"hello 1"。
处理过程:
- controller 返回一个DeferredResult,我们把它保存到内存里或者List里面(供后续访问)
- Spring MVC调用request.startAsync(),开启异步处理
- 与此同时将DispatcherServlet里的拦截器、Filter等等都马上退出主线程,但是response仍然保持打开的状态
- 应用通过另外一个线程(可能是MQ消息、定时任务等)给DeferredResult set值。然后Spring MVC会把这个请求再次派发给servlet容器
- DispatcherServlet再次被调用,然后处理后续的标准流程
3.模拟场景:接口接收请求,推送到队列receiveQueue,后台线程处理完成后推送到resultQueue,监听器监听resultQueue将结果赋值给DeferredResult,接口响应结果。
首先定义类Task:public class Task{ private DeferredResult result; private T message; private Boolean isTimeout;
定义MockQueue,用于管理队列及处理数据:
@Componentpublic class MockQueue { /** * 接收队列 */ private BlockingQueue> receiveQueue = new LinkedBlockingDeque<>(5000); /** * 结果队列 */ private BlockingQueue > resultQueue = new LinkedBlockingDeque<>(5000); public MockQueue() { this.run(); } /** * 接收task * * @param task task实体 * @throws InterruptedException */ public void put(Task task) throws InterruptedException { receiveQueue.put(task); } /** * 获取结果 * * @return * @throws InterruptedException */ public Task get() throws InterruptedException { return resultQueue.take(); } private void run() { new Thread(() -> { while (true) { try { Task task = receiveQueue.take(); System.out.println("receive data,start process!"); Thread.sleep(1000); task.setMessage("success"); //任务超时,跳过 if (task.getIsTimeout()) { continue; } resultQueue.put(task); System.out.println("process done!"); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); }}
然后实现Controller异步接口:
@Controllerpublic class DeferredResultQueueController { @Autowired MockQueue queue; @ResponseBody @GetMapping("/test") public DeferredResulttest(@RequestParam Integer id) throws InterruptedException { System.out.println("start test"); DeferredResult deferredResult = new DeferredResult<>(); Task task = new Task<>(deferredResult, "任务", false); deferredResult.onTimeout(() -> { System.out.println("任务超时 id=" + id); task.setMessage("任务超时"); task.setIsTimeout(true); }); queue.put(task); return deferredResult; }}
最后定义监听器,将resultQueue的结果写入DeferredResult。
@Componentpublic class QueueResultListener implements ApplicationListener{ @Autowired MockQueue mockQueue; @Override public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { new Thread(() -> { try { Task task = mockQueue.get(); task.getResult().setResult(task.getMessage()); System.out.println("监听器获取到结果:task=" + task); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); }}
三、参考资料
发表评论
最新留言
做的很好,不错不错
[***.243.131.199]2025年05月07日 16时44分55秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
文件结束符EOF
2019-03-17
Latex 错误集合
2019-03-17
利用VNC进行Windows和Linux之间的复制
2019-03-17
Python中什么时候用logger.debug, info, error
2019-03-17
Python的内置函数(四十一)、 index()
2019-03-17
Python的内置函数(四十二)、 numel()
2019-03-17
Python 代码占多行
2019-03-17
cuda error:device-side assert triggered
2019-03-17
TypeError: string indices must be integers
2019-03-17
卷积神经网络的工程技巧总结
2019-03-17
Python的内置函数(十六)、strip()
2019-03-17
Python字符串操作之字符串分割与组合
2019-03-17
tf.parse_single_example()
2019-03-17
latex表示极限
2019-03-17
tf.tuple
2019-03-17
C++实现二叉树的最近公共祖先
2019-03-17
CentOS7安装mysql5.6
2019-03-17