springboot+vue实现长轮询
长轮询和短轮询应用场景通常需要实时刷新数据时,需要用到轮询,字面意思就是不断重复请求。短轮询实现实时刷新最简单的实现就是短轮询,实现方式就是隔一段时间发送一次请求,进行数据刷新,实现很简单,代码如下以上就是断轮询代码,后台无需处理长轮询代码长轮询需要后台配合,后台代码如下:...
·
长轮询和短轮询应用场景
通常需要实时刷新数据时,需要用到轮询,字面意思就是不断重复请求。短轮询实现
实时刷新最简单的实现就是短轮询,实现方式就是隔一段时间发送一次请求,进行数据刷新,实现很简单,代码如下(这里不沾代码了,相信看到这个博文的对定时器,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拦截更新方法,只刷新内容改变的节点。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐
所有评论(0)