SpringBoot的DeferredResult案例:DeferredResult的超時處理方式
DeferredResult的超時處理,采用委托機(jī)制,也就是在實(shí)例DeferredResult時給予一個超時時長(毫秒),同時在onTimeout中委托(傳入)一個新的處理線程(我們可以認(rèn)為是超時線程);當(dāng)超時時間到來,DeferredResult啟動超時線程,超時線程處理業(yè)務(wù),封裝返回?cái)?shù)據(jù),給DeferredResult賦值(正確返回的或錯誤返回的)。
這個實(shí)例可以對上一個實(shí)例的代碼稍作改動即可。
一、增加超時處理任務(wù)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請求中注冊超時任務(wù)處理
修改第一個請求,修改了兩處,請自己比較
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:請求參數(shù){}", req.getParam());
DeferredResult<ResponseMsg<String>> result =new DeferredResult<ResponseMsg<String>>(10000l);//10秒
result.onTimeout(new TimeOutWork(result));//超時任務(wù)
requestBodyMap.put(1, req);// 把請求放到第一個請求map中
responseBodyMap.put(1, result);// 把請求響應(yīng)的DeferredResult實(shí)體放到第一個響應(yīng)map中
return result;
}
/**
* 第二個請求
*
* @param req
* @return
*/
@RequestMapping("/request2")
@ResponseBody
public DeferredResult<ResponseMsg<String>> request2(RequestMsg req) {
logger.debug("request2:請求參數(shù){}", req.getParam());
DeferredResult<ResponseMsg<String>> result = new DeferredResult<ResponseMsg<String>>();
requestBodyMap.put(2, req);// 把請求放到第二個請求map中
responseBodyMap.put(2, result);// 把請求響應(yīng)的DeferredResult實(shí)體放到第二個響應(yīng)map中
return result;
}
/**
* 第三個請求
*
* @param req
* @return
*/
@RequestMapping("/request3")
@ResponseBody
public DeferredResult<ResponseMsg<String>> request3(RequestMsg req) {
logger.debug("request3:請求參數(shù){}", req.getParam());
DeferredResult<ResponseMsg<String>> result = new DeferredResult<ResponseMsg<String>>();
requestBodyMap.put(3, req);// 把請求放到第三個請求map中
responseBodyMap.put(3, result);// 把請求響應(yīng)的DeferredResult實(shí)體放到第三個響應(yīng)map中
return result;
}
/**
* 控制第x個請求執(zhí)行返回操作,同時自己也返回同樣的值
*
* @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:請求參數(shù){}", x);
DeferredResult<ResponseMsg<String>> result = responseBodyMap.get(x);
if (result == null) {
msg.fail("錯誤!請求已經(jīng)釋放");
return msg;
}
String resultStr = "result" + x.toString() + ". Received:" + requestBodyMap.get(x).getParam();
msg.success("成功", resultStr);
result.setResult(msg);// 設(shè)置DeferredResult的結(jié)果值,設(shè)置之后,它對應(yīng)的請求進(jìn)行返回處理
responseBodyMap.remove(x);// 返回map刪除
logger.debug("requestXReturn--2:請求參數(shù){}", x);
logger.debug("requestXReturn--3:返回參數(shù){}", 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);
}
});
};
前后的代碼都省略了,其實(shí)僅僅修改了
if (data.status==0){
$("#request1ResultId").val(data.data);
} else {
$("#request1ResultId").val(data.msg);
}
四、小結(jié)
DeferredResult的超時處理比較簡單,定義時長及注冊一個處理Runnable實(shí)例即可。對于setResult、setErrorResult還需要繼續(xù)研究。
1、setResult

2、setErrorResult

3、isSetOrExpired

補(bǔ)充:解決了DeferredResult請求長時間占用數(shù)據(jù)庫連接的問題
最近看了看開源項(xiàng)目appllo配置中心的源碼,發(fā)現(xiàn)一個很有意思的東東:
(1)原理:由于使用了DeferredResult,根據(jù)Spring DispatcherServlet的默認(rèn)邏輯,數(shù)據(jù)庫連接只有在異步請求真正返回給客戶端的時候才會釋放回連接池
(2)應(yīng)用場景:長連接時間很長,對于大部分請求可能都要數(shù)小時以上才會返回。在這么長的一段時間內(nèi)一直占用著數(shù)據(jù)庫連接是不合理的
長連接場景解決:
@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());
}
}
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。如有錯誤或未考慮完全的地方,望不吝賜教。
相關(guān)文章
全面剖析java 數(shù)據(jù)類型與運(yùn)算符
這篇文章主要介紹了Java基本數(shù)據(jù)類型和運(yùn)算符,結(jié)合實(shí)例形式詳細(xì)分析了java基本數(shù)據(jù)類型、數(shù)據(jù)類型轉(zhuǎn)換、算術(shù)運(yùn)算符、邏輯運(yùn)算符等相關(guān)原理與操作技巧,需要的朋友可以參考下2021-09-09
在IntelliJ IDEA中創(chuàng)建和運(yùn)行java/scala/spark程序的方法
這篇文章主要介紹了在IntelliJ IDEA中創(chuàng)建和運(yùn)行java/scala/spark程序的教程,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-05-05
MyBatis 多個條件使用Map傳遞參數(shù)進(jìn)行批量刪除方式
這篇文章主要介紹了MyBatis 多個條件使用Map傳遞參數(shù)進(jìn)行批量刪除方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12
java + dom4j.jar提取xml文檔內(nèi)容
這篇文章主要為大家詳細(xì)介紹了java + dom4j.jar提取xml文檔內(nèi)容,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-08-08
Springboot項(xiàng)目快速實(shí)現(xiàn)過濾器功能
上篇文章已經(jīng)給大家介紹了Springboot項(xiàng)目如何快速實(shí)現(xiàn)Aop功能,這篇文章給大家介紹Springboot項(xiàng)目如何快速實(shí)現(xiàn)過濾器功能,感興趣的小伙伴可以參考閱讀2023-03-03
SpringBoot整合JWT實(shí)戰(zhàn)教程
JWT(JSON?Web?Token)是一種用于身份驗(yàn)證和授權(quán)的開放標(biāo)準(zhǔn)(RFC?7519),它使用JSON格式傳輸信息,可以在不同系統(tǒng)之間安全地傳遞數(shù)據(jù),這篇文章主要介紹了SpringBoot整合JWT實(shí)戰(zhàn)教程,需要的朋友可以參考下2023-06-06
Spring Boot 中application.yml與bootstrap.yml的區(qū)別
其實(shí)yml和properties文件是一樣的原理,且一個項(xiàng)目上要么yml或者properties,二選一的存在。這篇文章給大家介紹了Spring Boot 中application.yml與bootstrap.yml的區(qū)別,感興趣的朋友一起看看吧2018-04-04

