背景:
需求:
可以实时获取啄木鸟伍迪的访问数据、排名、积分等数据,可以实时现在在网站后台页面的大屏上;
解决方案:
可以使用异步请求,springmvc默认的请求都是同步的,也就是请求过去,必须得有处理完成,否则就回阻塞;异步请求是当发起一个请求,可以暂时没有响应,请求回被挂起不阻塞;请求过去,就返回一个DeferredResult,该DeferredResult可以在其他线程中返回需要的结果,也就是执行DeferredResult.setResult(),此时,异步请求回被唤醒
;
DeferredResult的流程:
- 浏览器发起异步请求
- 请求到达服务端被挂起
- 向浏览器进行响应,分为两种情况:
3.1 调用DeferredResult.setResult()
,请求被唤醒,返回结果
3.2 超时,返回一个你设定的结果 - 浏览得到响应,再次重复1,处理此次响应结果
如果定时任务去获取啄木鸟伍迪的访问数据、排名、积分等数据,当前定时任务处理完了,就告诉页面有数据了,马上ajax查询最新数据;页面重新执行ajax 的查询数据的函数即可;
官网给的例子:
@GetMapping("/quotes")
@ResponseBody
public DeferredResult<String> quotes() {
DeferredResult<String> deferredResult = new DeferredResult<>();
// 返回一个DeferredResult
return deferredResult;
}
// 其他线程处理结果;
deferredResult.setResult(result);
DeferredResult的使用步骤:
1.spring开启异步请求:
需要在web.xml 的filter 和DispatcherServlet 中配置 :<async-supported>true</async-supported>;
配置方式可以参考
2.添加异步请求
/**
* 异步请求
* @return
*/
@RequestMapping("/quotes")
@ResponseBody
public DeferredResult<String> quotes() {
//设置了1分钟超时
DeferredResult<String> deferredResult = new DeferredResult<>(60000L);
//放入缓存中,可全局调用
CacheUtils.put(CacheUtils.DEFERRED_CACHE_NAME, ShiroUtils.getUsername(),deferredResult);
return deferredResult;
}
3.页面的异步请求:
function updateDashboardData(){ //循环发送异步请求;
//设置了定时器,防止页面崩溃;
if(!!timeUpdate.quotesTimeout){
clearTimeoutFun(timeUpdate.quotesTimeout);
}
$.ajax({
type : 'post',
url : basePath + '/dashboard/quotes',//异步请求
contentType : "application/json;charset=utf-8",
success : function(data) {
if (!!data && data=="update") {
updateData();//更新数据;
}
timeUpdate.quotesTimeout = setTimeout(updateDashboardData, 5000);
},
error:function(){
timeUpdate.quotesTimeout = setTimeout(updateDashboardData, 5000);
}
});
}
4.定时任务调用DeferredResult.setResult(),唤起异步请求;(代码片段)
/**
* 添加博客园每日数据
*/
private void processMain() {
...........
.................
logger.info("[TASK][CnblogDataDayTask].OTHER.DATA.PROCESS.END");
// 批量添加
logger.info("[TASK][CnblogDataDayTask].DELETE.DATA.DAY.EXIST");
addDayList = resultList.stream().filter(s -> s.getAddDay() != null).map(CnblogDataDay::getAddDay)
.collect(Collectors.toList());
cnblogDataDayService.delete(addDayList);
logger.info("[TASK][CnblogDataDayTask].INSERT.DATA.DAY");
if(CollectionUtils.isNotEmpty(resultList)){
cnblogDataDayService.insert(resultList);
// 更新当前处理过的数据的 修改时间
logger.info("[TASK][CnblogDataDayTask].UPDATE.PROCESS.DATA");
cnblogDataDayService.updateModifyDate(endDate,DateUtils.toDate(syncTime, DateUtils.DATETIME_FORMAT));
updateBigScreem();
}
}
.....................
..........................
..................................
}
private void updateBigScreem() {
List<Object> cacheKeys = CacheUtils.getKeys(CacheUtils.DEFERRED_CACHE_NAME);
List<String> keys = getContainKey(cacheKeys, ShiroUtils.getUsername());
for (String key : keys) {
Object obj = CacheUtils.get(CacheUtils.DEFERRED_CACHE_NAME, key);
if (obj instanceof DeferredResult) {
DeferredResult<String> result = (DeferredResult<String>)obj;
result.setResult("update");
}
}
}
效果:
当请发起请求以后,执行了异步请求,且等待响应,如果此时执行了定时任务,如果此时执行了定时任务(在1分钟之内),那么该异步请求就处理完成了。