温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

SpringBoot项目中DeferredResult出现超时如何处理

发布时间:2021-01-20 14:23:01 来源:亿速云 阅读:1418 作者:Leah 栏目:开发技术

SpringBoot项目中DeferredResult出现超时如何处理?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。

DeferredResult的超时处理,采用委托机制,也就是在实例DeferredResult时给予一个超时时长(毫秒),同时在onTimeout中委托(传入)一个新的处理线程(我们可以认为是超时线程);当超时时间到来,DeferredResult启动超时线程,超时线程处理业务,封装返回数据,给DeferredResult赋值(正确返回的或错误返回的)。

一、增加超时处理任务TimeOutWork

package com.example; 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.async.DeferredResult;
 
public class TimeOutWork implements Runnable{ 
 private final Logger logger = LoggerFactory.getLogger(this.getClass());
 private DeferredResult<ResponseMsg<String>> deferredResult;
 
 public TimeOutWork(DeferredResult<ResponseMsg<String>> deferredResult) {
 this.deferredResult = deferredResult;
 }
 
 @Override
 public void run() {
 logger.debug("我超时啦!");
 ResponseMsg<String> msg = new ResponseMsg<String>();
 msg.fail("我超时啦!");
 //deferredResult.setResult(msg);
 deferredResult.setErrorResult(msg);
 }
}

二、DeferredResult请求中注册超时任务处理

修改第一个请求,修改了两处,请自己比较

package com.example; 
import java.util.HashMap;
import java.util.Map; 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
 
@RestController
@RequestMapping("/api")
public class DeferredRestController {
 
 private final Logger logger = LoggerFactory.getLogger(this.getClass()); 
 private final Map<Integer, DeferredResult<ResponseMsg<String>>> responseBodyMap = new HashMap<Integer, DeferredResult<ResponseMsg<String>>>();
 private final Map<Integer, RequestMsg> requestBodyMap = new HashMap<Integer, RequestMsg>();
 
 /**
 * 第一个请求
 * 
 * @param req
 * @return
 */
 @RequestMapping("/request1")
 @ResponseBody
 public DeferredResult<ResponseMsg<String>> request1(RequestMsg req) {
 logger.debug("request1:请求参数{}", req.getParam());
 DeferredResult<ResponseMsg<String>> result =new DeferredResult<ResponseMsg<String>>(10000l);//10秒
 result.onTimeout(new TimeOutWork(result));//超时任务
 requestBodyMap.put(1, req);// 把请求放到第一个请求map中
 responseBodyMap.put(1, result);// 把请求响应的DeferredResult实体放到第一个响应map中
 return result;
 }
 
 /**
 * 第二个请求
 * 
 * @param req
 * @return
 */
 @RequestMapping("/request2")
 @ResponseBody
 public DeferredResult<ResponseMsg<String>> request2(RequestMsg req) {
 logger.debug("request2:请求参数{}", req.getParam());
 DeferredResult<ResponseMsg<String>> result = new DeferredResult<ResponseMsg<String>>();
 requestBodyMap.put(2, req);// 把请求放到第二个请求map中
 responseBodyMap.put(2, result);// 把请求响应的DeferredResult实体放到第二个响应map中
 return result;
 }
 
 /**
 * 第三个请求
 * 
 * @param req
 * @return
 */
 @RequestMapping("/request3")
 @ResponseBody
 public DeferredResult<ResponseMsg<String>> request3(RequestMsg req) {
 logger.debug("request3:请求参数{}", req.getParam());
 DeferredResult<ResponseMsg<String>> result = new DeferredResult<ResponseMsg<String>>();
 requestBodyMap.put(3, req);// 把请求放到第三个请求map中
 responseBodyMap.put(3, result);// 把请求响应的DeferredResult实体放到第三个响应map中
 return result;
 }
 
 /**
 * 控制第x个请求执行返回操作,同时自己也返回同样的值
 * 
 * @param x
 * @return
 */
 @RequestMapping(value = "/requestXReturn", method = RequestMethod.POST)
 @ResponseBody
 public ResponseMsg<String> request1Return(Integer x) {
 ResponseMsg<String> msg = new ResponseMsg<String>();
 logger.debug("requestXReturn--1:请求参数{}", x);
 DeferredResult<ResponseMsg<String>> result = responseBodyMap.get(x);
 if (result == null) {
 msg.fail("錯誤!请求已经释放");
 return msg;
 }
 String resultStr = "result" + x.toString() + ". Received:" + requestBodyMap.get(x).getParam();
 msg.success("成功", resultStr);
 result.setResult(msg);// 设置DeferredResult的结果值,设置之后,它对应的请求进行返回处理
 responseBodyMap.remove(x);// 返回map删除
 logger.debug("requestXReturn--2:请求参数{}", x);
 logger.debug("requestXReturn--3:返回参数{}", msg);
 return msg;
 }
}

三、修改页面index.html

<script th:src="@{jquery-1.12.4.min.js}" type="text/javascript"></script>
 <script th:inline="javascript">
 function button1RequestClick(){
 var param=$("#request1RequestId").val();
 $.ajax({
  type:'post',
 url:'/api/request1',
 dataType : 'json',
 data : {
 'param' : param
 },
 success : function(data) {
 console.log(data);
 if (data.status==0){
  $("#request1ResultId").val(data.data);
 } else {
  $("#request1ResultId").val(data.msg);
 }
 },
 error : function(data) {
 console.log("button1RequestClick---error");
 console.log(data);
 //alert("错误消息:" + data);
 }
 });
 };

前后的代码都省略了,其实仅仅修改了

if (data.status==0){
 $("#request1ResultId").val(data.data);
} else {
 $("#request1ResultId").val(data.msg);
}

四、小结

DeferredResult的超时处理比较简单,定义时长及注册一个处理Runnable实例即可。对于setResult、setErrorResult还需要继续研究。

1、setResult

SpringBoot项目中DeferredResult出现超时如何处理

2、setErrorResult

SpringBoot项目中DeferredResult出现超时如何处理

3、isSetOrExpired

SpringBoot项目中DeferredResult出现超时如何处理

补充:解决了DeferredResult请求长时间占用数据库连接的问题

最近看了看开源项目appllo配置中心的源码,发现一个很有意思的东东:

(1)原理:由于使用了DeferredResult,根据Spring DispatcherServlet的默认逻辑,数据库连接只有在异步请求真正返回给客户端的时候才会释放回连接池

(2)应用场景:长连接时间很长,对于大部分请求可能都要数小时以上才会返回。在这么长的一段时间内一直占用着数据库连接是不合理的

长连接场景解决:

@Component
public class EntityManagerUtil extends EntityManagerFactoryAccessor {
 private static final Logger logger = LoggerFactory.getLogger(EntityManagerUtil.class);
 /**
 * close the entity manager.
 * Use it with caution! This is only intended for use with async request, which 
 * Spring won't close the entity manager until the async request is finished.
 */
 public void closeEntityManager() {
 EntityManagerHolder emHolder = (EntityManagerHolder)
 TransactionSynchronizationManager.getResource(getEntityManagerFactory());
 if (emHolder == null) {
 return;
 }
 logger.debug("Closing JPA EntityManager in EntityManagerUtil");
 EntityManagerFactoryUtils.closeEntityManager(emHolder.getEntityManager());
 }
}

看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI