SpringBoot的DeferredResult案例:DeferredResult的超時(shí)處理方式
DeferredResult的超時(shí)處理,采用委托機(jī)制,也就是在實(shí)例DeferredResult時(shí)給予一個(gè)超時(shí)時(shí)長(毫秒),同時(shí)在onTimeout中委托(傳入)一個(gè)新的處理線程(我們可以認(rèn)為是超時(shí)線程);當(dāng)超時(shí)時(shí)間到來,DeferredResult啟動超時(shí)線程,超時(shí)線程處理業(yè)務(wù),封裝返回?cái)?shù)據(jù),給DeferredResult賦值(正確返回的或錯(cuò)誤返回的)。
這個(gè)實(shí)例可以對上一個(gè)實(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("我超時(shí)啦!"); ResponseMsg<String> msg = new ResponseMsg<String>(); msg.fail("我超時(shí)啦!"); //deferredResult.setResult(msg); deferredResult.setErrorResult(msg); } }
二、DeferredResult請求中注冊超時(shí)任務(wù)處理
修改第一個(gè)請求,修改了兩處,請自己比較
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>(); /** * 第一個(gè)請求 * * @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));//超時(shí)任務(wù) requestBodyMap.put(1, req);// 把請求放到第一個(gè)請求map中 responseBodyMap.put(1, result);// 把請求響應(yīng)的DeferredResult實(shí)體放到第一個(gè)響應(yīng)map中 return result; } /** * 第二個(gè)請求 * * @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);// 把請求放到第二個(gè)請求map中 responseBodyMap.put(2, result);// 把請求響應(yīng)的DeferredResult實(shí)體放到第二個(gè)響應(yīng)map中 return result; } /** * 第三個(gè)請求 * * @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);// 把請求放到第三個(gè)請求map中 responseBodyMap.put(3, result);// 把請求響應(yīng)的DeferredResult實(shí)體放到第三個(gè)響應(yīng)map中 return result; } /** * 控制第x個(gè)請求執(zhí)行返回操作,同時(shí)自己也返回同樣的值 * * @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("錯(cuò)誤!請求已經(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("錯(cuò)誤消息:" + data); } }); };
前后的代碼都省略了,其實(shí)僅僅修改了
if (data.status==0){ $("#request1ResultId").val(data.data); } else { $("#request1ResultId").val(data.msg); }
四、小結(jié)
DeferredResult的超時(shí)處理比較簡單,定義時(shí)長及注冊一個(gè)處理Runnable實(shí)例即可。對于setResult、setErrorResult還需要繼續(xù)研究。
1、setResult
2、setErrorResult
3、isSetOrExpired
補(bǔ)充:解決了DeferredResult請求長時(shí)間占用數(shù)據(jù)庫連接的問題
最近看了看開源項(xiàng)目appllo配置中心的源碼,發(fā)現(xiàn)一個(gè)很有意思的東東:
(1)原理:由于使用了DeferredResult,根據(jù)Spring DispatcherServlet的默認(rèn)邏輯,數(shù)據(jù)庫連接只有在異步請求真正返回給客戶端的時(shí)候才會釋放回連接池
(2)應(yīng)用場景:長連接時(shí)間很長,對于大部分請求可能都要數(shù)小時(shí)以上才會返回。在這么長的一段時(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()); } }
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。
相關(guān)文章
基于idea把springboot項(xiàng)目部署到docker
這篇文章主要介紹了基于idea把springboot項(xiàng)目部署到docker,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01spring boot整合redis實(shí)現(xiàn)shiro的分布式session共享的方法
本篇文章主要介紹了spring boot整合redis實(shí)現(xiàn)shiro的分布式session共享的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-01-0114個(gè)編寫Spring MVC控制器的實(shí)用小技巧(吐血整理)
這篇文章主要介紹了14個(gè)編寫Spring MVC控制器的實(shí)用小技巧(吐血整理),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11Java中報(bào)錯(cuò)org.springframework.jdbc.UncategorizedSQLException的多種
本文主要介紹了Java中報(bào)錯(cuò)org.springframework.jdbc.UncategorizedSQLException的多種解決方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06利用Java實(shí)現(xiàn)網(wǎng)站聚合工具
互聯(lián)網(wǎng)上有數(shù)以萬億計(jì)的網(wǎng)站,每個(gè)網(wǎng)站大都具有一定的功能。搜索引擎雖然對互聯(lián)網(wǎng)上的部分網(wǎng)站建立了索引,但是其作為一個(gè)大而全的搜索系統(tǒng),無法很好的定位到一些特殊的需求。因此本文將介紹一個(gè)用java實(shí)現(xiàn)的網(wǎng)站數(shù)據(jù)聚合工具,需要的可以參考一下2022-01-01關(guān)于Hystrix的監(jiān)控及可視化面板
這篇文章主要介紹了關(guān)于Hystrix的監(jiān)控及可視化面板,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08