您好,登錄后才能下訂單哦!
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); } }
修改第一個請求,修改了兩處,請自己比較
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; } }
<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
2、setErrorResult
3、isSetOrExpired
補充:解決了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()); } }
看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。