springboot+vue实现长轮询

本文介绍了短轮询和长轮询两种实现实时数据刷新的技术手段。短轮询通过定时发送请求来更新数据;而长轮询则利用后台挂起请求,待有数据更新后再响应,减少了不必要的请求,适用于数据更新频率较低的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

长轮询和短轮询应用场景

通常需要实时刷新数据时,需要用到轮询,字面意思就是不断重复请求。

短轮询实现

实时刷新最简单的实现就是短轮询,实现方式就是隔一段时间发送一次请求,进行数据刷新,实现很简单,代码如下(这里不沾代码了,相信看到这个博文的对定时器,ajax请求很熟悉了)

在这里插入图片描述

在这里插入图片描述

以上就是短轮询代码,后台无需处理

长轮询代码

长轮询需要后台配合,后台代码如下:

@GetMapping("/getMapChange")
public List<MapManager.MapResult> getMapChange(HttpServletRequest request) {
    UserOutPut user = (UserOutPut) request.getSession().getAttribute(LoginInterceptor.USER_KEY);
    try {
        mapManager.register(user);
        return mapManager.pollAll(user);
    }catch (Exception e){
        throw new IllegalStateException();
    }finally {
        mapManager.remove(user);
    }
}

截图解释:
在这里插入图片描述
下面看看 我这里消息管理类 MapManager中的代码,register pollAll 方法都做了什么

public class MapManager {
//保留消息的map key是请求时注册的,value是注册的请求要收到的消息
    private static final Map<UserOutPut, Queue<MapResult>> MESSAGE_MAP = new ConcurrentHashMap<>();
    private static final Map<UserOutPut, Integer> RETYR_TIME = new ConcurrentHashMap<>();

	//注册方法,只有注册了请求才会接收到消息
    public void register(UserOutPut token) {
        MESSAGE_MAP.computeIfAbsent(token,k-> new ConcurrentLinkedQueue());
        RETYR_TIME.computeIfAbsent(token,k -> 0);
    }

	//无论异常,还是空消息,最终都要将注册的请求销毁,防止驻留在map内存中。
    public void remove(UserOutPut token){
        MESSAGE_MAP.remove(token);
        RETYR_TIME.remove(token);
    }

//将消息添加到所有请求的value中,这里参数只是我用到的消息结构
    public void push(Node node, NodeInfo nodeInfo) {
        MESSAGE_MAP.keySet().forEach(k -> {
            synchronized (k) {
                MapResult manChange = new MapResult(node,nodeInfo);
                MESSAGE_MAP.get(k).add(manChange);
                k.notifyAll();//唤醒该请求对应的pollAll方法中的睡眠
            }
        });
    }

//根据请求弹出消息返回给前台的请求
    public List<MapResult> pollAll(UserOutPut token) {
        List<MapResult> result = new ArrayList<>(); //最终的返回结果
        Queue<MapResult> hashMaps = MESSAGE_MAP.get(token);//根据请求的key值,得到消息队列
        if (hashMaps != null) {
            while (true) {
                MapResult poll = hashMaps.poll();//弹出消息,并添加到结果集中
                if (poll == null) {//消息为空时,表示队列中无消息了,退出循环即可
                    break;
                }
                result.add(poll);
            }
        }else {
            register(token);
        }
        if (result.isEmpty() && RETYR_TIME.get(token) < 3) {//如果结果集为空,就将请求挂起先不响应前台的请求,
        //这也是长轮询的核心,目的就是不响应前台,等有消息再向应,这里的第二个条件只是为了等待消息超时直接返回空响应给前台,这个超时可根据自己情况定
            try {
                Integer time = RETYR_TIME.get(token);
                RETYR_TIME.put(token,time+1);
                synchronized (token) { //没有消息该线程进入等待,释放资源占用
                //此处token用于被唤醒,唤醒位置在该类的push方法中
                    token.wait(10000);
                }
                return pollAll(token);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        return result;
    }
	
	/**
	*这里是我的返回结果类,可根据情况自定义
	*/
    @Data
    @NoArgsConstructor
    public class MapResult{
        private Node node;
        private NodeInfo nodeInfo;

        public MapResult(Node node,NodeInfo nodeInfo) {
            this.node = node;
            this.nodeInfo = nodeInfo;
        }
    }
}

这里在附上我的push消息位置,用到了个aop,代码如下(这里直接截图,不附代码了,因为什么时候添加消息看个人应用场景):
在这里插入图片描述
后台代码就以上这些,最后就是前台请求代码了:
在这里插入图片描述前台操作目的就是等请求返回了,再次发起,减少请求次数,熟悉vue的,这个方法只要在vue的create函数中调用一次即可,无需计时器。

小结:

应用场景:
因为我这里要实时刷新一个三维数组地图,但是地图节点信息改变又没那么频繁,所以 采用长轮询是最好的选择,因为短轮询会造成很多请求浪费。
为什么长轮询在处理返回结果中调用不会发生栈溢出(个人想的问题,可能有点幼稚了):
因为ajax是异步请求,实时上请求发出后,该请求方法已经结束,处理请求的方法是新的栈,所以不在同一方法内部调用,所以不会溢出。
我这里aop的使用:
因为我的地图是三维数组,小的库也要近千个货位,三维数据的遍历是三层循环,如果每次都把地图全部刷新,浪费效率。所以我这里aop拦截更新方法,只刷新内容改变的节点。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值