DeferredResult长轮询的应用

1.为什么要使用DeferredResult?

建立一次连接,让他们等待尽可能长的时间。这样同时如果有新的数据到达服务器,服务器可以直接返回响应。通过这种方式,我们绝对可以减少所涉及的请求和响应周期的数量。

1.背景:有个大屏展示了上百个资产的状态,当这个资产有告警产生的时候,对应资产需要展示为红色,让运维人员知道该资产出了问题。

2.原来的实现:前端每隔10秒就请求一次后端,后端再去查询mysql,再把最新的数据返回给前端

3.存在的问题:理论上可能会存在最多10秒的延迟,前端展示就会慢了0-10秒。

4.改进方法:DeferredResult长轮询,后端有更新后马上返回给前端

2.DeferredResult执行逻辑

  1. 浏览器发起异步请求
  2. 请求到达服务端被挂起(使用浏览器查看请求状态,此时为pending)
  3. 向浏览器进行响应,分为两种情况:
    3.1 调用DeferredResult.setResult(),请求被唤醒,返回结果
    3.2 超时,返回一个你设定的结果
  4. 浏览得到响应,再次重复1,处理此次响应结果

 3.实际应用

下面代码不是使用demo,是实际的应用记录,没用过的同学可能看起来比较费劲。demo这个大神的比较详细:Spring Boot实现长轮询和短轮询(Short Polling & Long Polling)长连接和短连接_jake_Aaron(小湮没)的博客-CSDN博客_长轮询如何实现

下面代码逻辑是只要有告警状态的变更,就从数据库中获取所有设备的最新状态,然后和redis中保存的比较,如果有更新,就调setResult方法,把结果返回给客户端。如果没有更新,就在20秒后跳出循环。

@ApiOperation("长轮询判断是否有最新告警")
    @GetMapping("/getWarnAndLevels")
    public DeferredResult getWarnInfos(int counter) {
        Map<String, Object> map = new HashMap<>();
        long timeOutInMilliSec = 25000;//超时时间25秒
        String timeOutResp = "超时了";
        DeferredResult deferredResult = new DeferredResult<>(timeOutInMilliSec, timeOutResp);
        Object warnAndLevel = redisUtil.get(StoConstant.warnAndLevel);//告警信息
        if (warnAndLevel == null) {//第一次,直接调原接口
            map.put(StoConstant.count, 0);
            map.put("updata", true);
            deferredResult.setResult(map);
            return deferredResult;
        }
        String warnAndLevelString = (String) warnAndLevel;
        List<GetWarnInfoResponseVo> warnAndLevelList = JSON.parseArray(warnAndLevelString, GetWarnInfoResponseVo.class);
        List<String> list = new LinkedList<>();
        long t1 = System.currentTimeMillis();
        while (true) {
            long t2 = System.currentTimeMillis();
            if (t2 - t1 > 20 * 1000) { //hold住请求20秒
                break;
            }
            warnAndLeveStatus.drainTo(list, 1);//一次只拿一个
            if (list.size() > 0) {
                List<GetWarnInfoResponseVo> warnInfoResponseVoList = warnInfoService.getWarnInfo();
                if (!(warnInfoResponseVoList.size() == warnAndLevelList.size() && warnInfoResponseVoList.containsAll(warnAndLevelList))) {
                    //不相同
                    String warnAndLevelJson = JSON.toJSONString(warnInfoResponseVoList);
                    redisUtil.set(StoConstant.warnAndLevel, warnAndLevelJson);
                    int counterRedis = redisUtil.get(StoConstant.warnAndLevelCounter) == null ? 0 : Integer.parseInt(redisUtil.get(StoConstant.warnAndLevelCounter).toString());
                    map.put(StoConstant.count, ++counterRedis);
                    map.put("updata", true);
                    redisUtil.incr(StoConstant.warnAndLevelCounter, 1);//增加1
                    deferredResult.setResult(map);
                    return deferredResult;
                }
            }
            int counterRedis = redisUtil.get(StoConstant.warnAndLevelCounter) == null ? 0 : Integer.parseInt(redisUtil.get(StoConstant.warnAndLevelCounter).toString());
            if (counter < counterRedis) {
                map.put(StoConstant.count, counterRedis);
                map.put("updata", true);
                deferredResult.setResult(map);
                return deferredResult;
            }
            try {
                Thread.sleep(3000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        map.put(StoConstant.count, counter);
        map.put("updata", false);
        deferredResult.setResult(map);//超时
        return deferredResult;
    }

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值