长轮询和短轮询应用场景
通常需要实时刷新数据时,需要用到轮询,字面意思就是不断重复请求。短轮询实现
实时刷新最简单的实现就是短轮询,实现方式就是隔一段时间发送一次请求,进行数据刷新,实现很简单,代码如下(这里不沾代码了,相信看到这个博文的对定时器,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函数中调用一次即可,无需计时器。