springmvc 使用 DeferredResult

发布时间 2023-08-16 17:48:16作者: 啄木鸟伍迪

背景:

需求:

可以实时获取啄木鸟伍迪的访问数据、排名、积分等数据,可以实时现在在网站后台页面的大屏上;

解决方案:

可以使用异步请求,springmvc默认的请求都是同步的,也就是请求过去,必须得有处理完成,否则就回阻塞;异步请求是当发起一个请求,可以暂时没有响应,请求回被挂起不阻塞;请求过去,就返回一个DeferredResult,该DeferredResult可以在其他线程中返回需要的结果,也就是执行DeferredResult.setResult(),此时,异步请求回被唤醒

DeferredResult的流程:

    1. 浏览器发起异步请求
    2. 请求到达服务端被挂起
    3. 向浏览器进行响应,分为两种情况:
      3.1 调用DeferredResult.setResult(),请求被唤醒,返回结果
      3.2 超时,返回一个你设定的结果
    4. 浏览得到响应,再次重复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>;

配置方式可以参考https://www.cnblogs.com/lixiuming521125/p/17622238.html

 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分钟之内),那么该异步请求就处理完成了。