SpringBoot的DeferredResult案例:DeferredResult的超时处
理⽅式
DeferredResult的超时处理,采⽤委托机制,也就是在实例DeferredResult时给予⼀个超时时长(毫秒),同时在onTimeout中委托(传⼊)⼀个新的处理线程(我们可以认为是超时线程);当超时时间到来,DeferredResult启动超时线程,超时线程处理业务,封装返回数据,给DeferredResult赋值(正确返回的或错误返回的)。
这个实例可以对上⼀个实例的代码稍作改动即可。
⼀、增加超时处理任务TimeOutWork
ample;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.t.request.async.DeferredResult;
public class TimeOutWork implements Runnable{
private final Logger logger = Class());
private DeferredResult<ResponMsg<String>> deferredResult;
public TimeOutWork(DeferredResult<ResponMsg<String>> deferredResult) {
this.deferredResult = deferredResult;
}
@Override
public void run() {
logger.debug("我超时啦!");
ResponMsg<String> msg = new ResponMsg<String>();
msg.fail("我超时啦!");
/
/deferredResult.tResult(msg);
deferredResult.tErrorResult(msg);
}
}
⼆、DeferredResult请求中注册超时任务处理
修改第⼀个请求,修改了两处,请⾃⼰⽐较
ample;
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.ResponBody;
import org.springframework.web.bind.annotation.RestController;
import org.t.request.async.DeferredResult;
@RestController
@RequestMapping("/api")
public class DeferredRestController {
private final Logger logger = Class());
private final Map<Integer, DeferredResult<ResponMsg<String>>> responBodyMap = new HashMap<Integer, DeferredResult<ResponMsg<String>>>();
private final Map<Integer, RequestMsg> requestBodyMap = new HashMap<Integer, RequestMsg>();
/**
* 第⼀个请求
*
* @param req
* @return
*/
@RequestMapping("/request1")
@ResponBody
public DeferredResult<ResponMsg<String>> request1(RequestMsg req) {
logger.debug("request1:请求参数{}", Param());
DeferredResult<ResponMsg<String>> result =new DeferredResult<ResponMsg<String>>(10000l);//10秒
小孩一直哭
requestBodyMap.put(1, req);// 把请求放到第⼀个请求map中
responBodyMap.put(1, result);// 把请求响应的DeferredResult实体放到第⼀个响应map中
return result;
}
/**
* 第⼆个请求
* @param req
* @return
*/
@RequestMapping("/request2")
@ResponBody
public DeferredResult<ResponMsg<String>> request2(RequestMsg req) {
logger.debug("request2:请求参数{}", Param());
DeferredResult<ResponMsg<String>> result = new DeferredResult<ResponMsg<String>>(); requestBodyMap.put(2, req);// 把请求放到第⼆个请求map中
responBodyMap.put(2, result);// 把请求响应的DeferredResult实体放到第⼆个响应map中
return result;
鬃狮}
/**
* 第三个请求
*
* @param req
* @return
*/
@RequestMapping("/request3")
@ResponBody
public DeferredResult<ResponMsg<String>> request3(RequestMsg req) {
logger.debug("request3:请求参数{}", Param());
DeferredResult<ResponMsg<String>> result = new DeferredResult<ResponMsg<String>>(); requestBodyMap.put(3, req);// 把请求放到第三个请求map中
responBodyMap.put(3, result);// 把请求响应的DeferredResult实体放到第三个响应map中
return result;
}
/**
下雪作文
* 控制第x个请求执⾏返回操作,同时⾃⼰也返回同样的值
*眼睛美容
* @param x
* @return
*/
@RequestMapping(value = "/requestXReturn", method = RequestMethod.POST)
@ResponBody
public ResponMsg<String> request1Return(Integer x) {
ResponMsg<String> msg = new ResponMsg<String>();
logger.debug("requestXReturn--1:请求参数{}", x);
DeferredResult<ResponMsg<String>> result = (x);大树妈妈简谱
if (result == null) {磁盘怎么分区
msg.fail("錯誤!请求已经释放");
return msg;
}
String resultStr = "result" + x.toString() + ". Received:" + (x).getParam();
msg.success("成功", resultStr);
result.tResult(msg);// 设置DeferredResult的结果值,设置之后,它对应的请求进⾏返回处理
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);
} el {
$("#request1ResultId").val(data.msg);
}
},
error : function(data) {
console.log("button1RequestClick---error");
console.log(data);
//alert("错误消息:" + data);
}
};
前后的代码都省略了,其实仅仅修改了
if (data.status==0){
$("#request1ResultId").val(data.data);
} el {
$("#request1ResultId").val(data.msg);
}
四、⼩结
DeferredResult的超时处理⽐较简单,定义时长及注册⼀个处理Runnable实例即可。对于tResult、tErrorResult还需要继续研究。
1、tResult
2、tErrorResult
3、isSetOrExpired
补充:解决了DeferredResult请求长时间占⽤数据库连接的问题
最近看了看开源项⽬appllo配置中⼼的源码,发现⼀个很有意思的东东:
(1)原理:由于使⽤了DeferredResult,根据Spring DispatcherServlet的默认逻辑,数据库连接只有在异步请求真正返回给客户端的时候才会释放回连接池
(2)应⽤场景:长连接时间很长,对于⼤部分请求可能都要数⼩时以上才会返回。在这么长的⼀段时间内⼀直占⽤着数据库连接是不合理的
长连接场景解决:
@Component
public class EntityManagerUtil extends EntityManagerFactoryAccessor {
private static final Logger logger = Logger(EntityManagerUtil.class);
/**
* clo the entity manager.
* U it with caution! This is only intended for u with async request, which
* Spring won't clo the entity manager until the async request is finished.
*/
public void cloEntityManager() {
EntityManagerHolder emHolder = (EntityManagerHolder)
if (emHolder == null) {
return;
}
logger.debug("Closing JPA EntityManager in EntityManagerUtil");
EntityManagerFactoryUtils.EntityManager());
苹果的作用}
}
以上为个⼈经验,希望能给⼤家⼀个参考,也希望⼤家多多⽀持。如有错误或未考虑完全的地⽅,望不吝赐教。