当前位置: 首页 > news >正文

Spring DeferredResult 实现长轮询

1、背景

在项目开发中,有一个流程性的方法执行,这个方法会调用各种方法,可能会导致时间比较长 ,如果一直等待响应结果的话,可能会造成超时,如果直接使用异步的方式的话,前端无法知道整体流程什么时候会结束,

2、解决方案

使用了DeferredResult 的方式,设置超时时间,当流程执行完了没有超过指定时间就可以直接返回结果,如果超过了指定时间就给前端先返回超时结果,并且指定一个唯一标志放到结果中返回,前端后续可以拿着这个唯一标志来轮询,知道返回执行完成

关于 DeferredResult :请求的处理线程(即 tomcat 线程池的线程)不会等DeferredResult#setResult() 被调用才释放,而是直接释放了

也就是说 tomcat 线程安排好 DeferredResult 的一些配置后,不会等逻辑处理完(DeferredResult->setResult()的调用或者超时)。

而是直接释放了,这样 tomcat 线程就被回收到线程池中了,可以响应其他请求,不会傻傻地阻塞等着 DeferredResult->setResult() 被调用或超时。

我们都知道 tomcat 的线程池大小是有限的,如果我们的一些业务逻辑处理慢的话,会渐渐地占满 tomcat 线程,这样就无法处理新的请求,所以一些处理缓慢的业务我们会放到业务线程池中处理,但单纯的放到业务线程池中处理的话,我们无法得知其什么时候处理完,也无法将处理完的结果和之前的请求匹配上,所以常做的方式就是轮询。

而 DeferredResult 的做法就类似仅把事情安排好,不会管事情做好没,tomcat 线程就释放走了,注意此时不会给请求方(如浏览器)任何响应,而是将请求存放在一边,等后面有结果了再把之前的请求拿来,把值响应给请求方。

用简单的话来总结下 Spring DeferredResult :如果返回值类型是 DeferredResult 则表明其是异步请求,tomcat 线程不会等到应用程序处理完或者超时,而是会立即释放线程。

而这个未处理完的请求则会暂存,tomcat 知晓其为异步请求,也不会对客户端进行响应,直至 tomcat 线程扫描到请求超时或者应用线程将 result 塞入到 DeferredResult 中。

3、一些常用方法

public void onTimeout(Runnable callback)

public void onError(Consumer<Throwable> callback)

public void onCompletion(Runnable callback)

public boolean setResult(T result)

  • onTimeout():仅超时触发。
  • onError():仅异步任务抛出异常时触发。
  • onCompletion() 是兜底回调,无论何种结束方式都会执行,适合释放共享资源(如移除缓存、关闭连接)
  • setResult() 设置返回结果集

4、具体简单代码实现

// -1 表示任务未完成 0 表示任务失败 其它是具体值private static final Map<String,String> EXEC_CACHE  = new ConcurrentHashMap<>();@Overridepublic DeferredResult<Map<String, String>> exec() {DeferredResult<Map<String, String>> deferredResult = new DeferredResult<>(5000L);// 设置超时回调(如果任务未在 2 秒内完成,返回超时响应)String flag = UUID.randomUUID().toString();Map<String, String> result = new HashMap<>();deferredResult.onTimeout(() -> {result.put("code", "410");result.put("message", "任务未完成,已超时!");result.put("flag",flag);deferredResult.setResult(result);EXEC_CACHE.put(flag, "-1");});//执行具体任务CompletableFuture.runAsync(()->{try {//模拟执行耗时Thread.sleep(1000);//模拟获到执行结果String execResult = UUID.randomUUID().toString();//放入缓存EXEC_CACHE.put(flag, execResult);//完成响应result.put("code", "200");result.put("message", "任务完成");result.put("flag",execResult);deferredResult.setResult(result);}catch (Exception e){//完成响应result.put("code", "500");result.put("message", "任务完成");result.put("flag",null);deferredResult.setResult(result);EXEC_CACHE.put(flag, "0");}});return deferredResult;}@Overridepublic String queryExecResult(String flag) {//前端根据具体值判断要不要继续轮询return EXEC_CACHE.get(flag);}

执行超时情况

轮询查询

未超时返回

http://www.lryc.cn/news/597438.html

相关文章:

  • [HarmonyOS] HarmonyOS LiteOS-A 设备开发全流程指南
  • 清华大学层次化空间记忆助力具身导航!Mem4Nav:基于层次化空间认知长短期记忆系统的城市环境视觉语言导航
  • 本地部署 Stable Diffusion:零基础搭建 AI文生图模型
  • 仓颉兴趣组优秀项目-Ginger
  • CPU,减少晶体管翻转次数的编码
  • opencv学习(视频读取)
  • 策略模式(Strategy Pattern)+ 模板方法模式(Template Method Pattern)的组合使用
  • AR维修辅助系统UI设计:虚实融合界面中的故障标注与操作引导
  • 设计模式(单例)
  • ar景区导航导览开发方案:核心技术架构与功能设计
  • HarmonyOS学习记录5
  • 第三章.Redis渐进式遍历
  • 计算机毕设分享-基于SpringBoot的房屋租赁系统(开题报告+源码+Lun文+开发文档+数据库设计文档)
  • 神经架构搜索革命:从动态搜索到高性能LLM的蜕变之路
  • Spark实现WorldCount执行流程图
  • 05-ES6
  • Unity UI的未来之路:从UGUI到UI Toolkit的架构演进与特性剖析(2)
  • UE5多人MOBA+GAS 29、创建一个等级UI
  • k8s中的configmap存储
  • K8s:离线部署Kubernetes1.26.12及采用外部Harbor
  • CSP-J系列【2023】P9751 [CSP-J 2023] 旅游巴士题解
  • 视频孪生赋能数字住建:构建智慧城市新蓝图​
  • 绿地集团携手深兰科技推动AI医诊大模型快速落地
  • 【补题】Codeforces Round 1000 (Div. 2) C. Remove Exactly Two
  • Python 爬虫(一):爬虫伪装
  • .NET-键控服务依赖注入
  • C 语言基础第9天:一、二维数组
  • 基于Python的新闻爬虫:实时追踪行业动态
  • 网络调制技术对比表
  • 【CNN】模型评估标准