SpringBoot的DeferredResult案例:DeferredResult的超时处理方式

作者:lxhjh 时间:2023-08-09 14:00:58 

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案例:DeferredResult的超时处理方式

2、setErrorResult

SpringBoot的DeferredResult案例:DeferredResult的超时处理方式

3、isSetOrExpired

SpringBoot的DeferredResult案例: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());
}
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。如有错误或未考虑完全的地方,望不吝赐教。

来源:https://blog.csdn.net/lxhjh/article/details/70240722

标签:SpringBoot,DeferredResult,DeferredResult,超时
0
投稿

猜你喜欢

  • Android操作存放在assets文件夹下SQLite数据库的方法

    2023-12-13 21:00:26
  • Spring boot集成Mybatis的方法教程

    2023-11-25 06:20:41
  • AQS同步组件Semaphore信号量案例剖析

    2023-11-27 14:27:04
  • java~springboot~ibatis数组in查询的实现方法

    2023-03-31 21:31:13
  • 使用HandlerMethodArgumentResolver用于统一获取当前登录用户

    2023-04-20 12:09:16
  • C#实现打字小游戏

    2023-11-08 01:08:04
  • java读取csv文件内容示例代码

    2023-03-13 22:09:14
  • 举例讲解Java设计模式编程中Decorator装饰者模式的运用

    2023-01-13 03:17:00
  • 如何在Android App中集成支付宝和微信支付功能

    2023-04-01 00:06:00
  • SpringBoot使用Redisson实现分布式锁(秒杀系统)

    2022-07-17 05:15:41
  • java switch语句使用注意的四大细节

    2022-07-10 10:46:04
  • C#开发中常用的加密解密方法汇总

    2021-09-06 23:35:49
  • SpringBoot 利用thymeleaf自定义错误页面

    2023-11-29 08:29:55
  • android 使用kotlin 实现点击更换全局语言(中日英切换)

    2023-11-18 03:08:50
  • Java实例讲解注解的应用

    2021-11-02 01:43:14
  • Android实现颜色渐变动画效果

    2022-05-31 09:52:53
  • java开发之Jdbc分页源码详解

    2021-10-28 16:06:48
  • C#开发WinForm根据条件改变DataGridView行颜色

    2022-05-06 05:38:56
  • 深入理解堆排序及其分析

    2022-05-09 00:16:09
  • C#键值对容器的介绍

    2023-04-14 12:26:56
  • asp之家 软件编程 m.aspxhome.com