WebSocket后端轮询代替Ajax轮询
Websocket服务端可以主动推送信息给客户端,解决了http轮询延迟的问题
同时解决服务器上消耗资源的问题
由于Websocket只需要一次HTTP握手,所以说整个通讯过程是建立在一次连接/状态中,也就避免了HTTP的非状态性,服务端会一直知道你的信息,直到你关闭请求
目前唯一的问题是:不兼容低版本的IE
后端轮询逻辑
@Scheduled(cron = "*/5 * * * * ?")//五秒轮询
public void TimeStampsSearch() {
try {
//第一次没有取到值时
if (currentTimeList == null) {
currentTimeList = webSocketService.getAllTimeStamps();
} else {
//当不是第一次,取到了值
List<WeiBoNewModel> newTimeList = webSocketService.getAllTimeStamps();
//循环判断时间是否修改了
for (int i = 0; i < currentTimeList.size(); i++) {
if (currentTimeList.size()!=newTimeList.size()) {
System.out.println("list长度不相等");
System.out.println("数据更新了");
socket.sendInfo("10", "1");
break;
}
if (!currentTimeList.get(i).getOnboardTime().equals(newTimeList.get(i).getOnboardTime())) {
System.out.println("数据时间不一致");
System.out.println("数据更新了");
socket.sendInfo("10", "1");
break;
}
}
currentTimeList = newTimeList;
}
} catch (Exception e) {
e.printStackTrace();
}
}
全部代码
注:本部分内容需要上部分的SpringBoot程序
https://blog.csdn.net/weixin_43596589/article/details/115655246
第一步
在service下创建WebSocketService .java
public interface WebSocketService {
List<WeiBoNewModel> getAllTimeStamps();
}
第二步
在mapper下创建WebSocketMapper.java
@Mapper
public interface WebSocketMapper {
List<WeiBoNewModel> getAllTimeStamps();
}
第三步
在serviceImpl下创建WebSocketServiceImpl .java
@Service("webSocketService")
public class WebSocketServiceImpl implements WebSocketService {
@Autowired
private WebSocketMapper webSocketMapper;
@Override
public List<WeiBoNewModel> getAllTimeStamps() {
return webSocketMapper.getAllTimeStamps();
}
}
第四步
在resources/mapper下创建WeiboMapper.xml
<mapper namespace="bx.springChart.demoProject.mapper.WebSocketMapper">
<resultMap id="WebSocketMap" type="bx.springChart.demoProject.model.WeiBoNewModel">
<result column="id" jdbcType="INTEGER" property="id"/>
<result column="onboard_time" jdbcType="BIGINT" property="onboardTime"/>
</resultMap>
<select id="getAllTimeStamps" resultMap="WebSocketMap">
SELECT * FROM db_weibo_content
</select>
</mapper>
第五步
新建socket文件夹,在其下创建WebSocketServer.java
package bx.springChart.demoProject.socket;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
@ServerEndpoint("/webSocket/{sid}")
@Component
public class WebSocketServer {
//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static AtomicInteger onlineNum = new AtomicInteger();
//concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketServer对象。
private static ConcurrentHashMap<String, Session> sessionPools = new ConcurrentHashMap<>();
//发送消息
public void sendMessage(Session session, String message) throws IOException {
if(session != null){
synchronized (session) {
session.getBasicRemote().sendText(message);
}
}
}
//给指定用户发送信息
public void sendInfo(String userName, String message){
Session session = sessionPools.get(userName);
try {
sendMessage(session, message);
}catch (Exception e){
e.printStackTrace();
}
}
//建立连接成功调用
@OnOpen
public void onOpen(Session session, @PathParam(value = "sid") String userName){
sessionPools.put(userName, session);
addOnlineCount();
System.out.println(userName + "加入webSocket!当前人数为" + onlineNum);
try {
sendMessage(session, "欢迎" + userName + "加入连接!");
} catch (IOException e) {
e.printStackTrace();
}
}
//关闭连接时调用
@OnClose
public void onClose(@PathParam(value = "sid") String userName){
sessionPools.remove(userName);
subOnlineCount();
System.out.println(userName + "断开webSocket连接!当前人数为" + onlineNum);
}
//收到客户端信息
@OnMessage
public void onMessage(String message) throws IOException{
message = "客户端:" + message + ",已收到";
System.out.println(message);
for (Session session: sessionPools.values()) {
try {
sendMessage(session, message);
} catch(Exception e){
e.printStackTrace();
continue;
}
}
}
//错误时调用
@OnError
public void onError(Session session, Throwable throwable){
System.out.println("发生错误");
throwable.printStackTrace();
}
public static void addOnlineCount(){
onlineNum.incrementAndGet();
}
public static void subOnlineCount() {
onlineNum.decrementAndGet();
}
}
第六步
新建repository文件夹,在其下创建WebSocketConfig.java
package bx.springChart.demoProject.repository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
/**
* ServerEndpointExporter 作用
*
* 这个Bean会自动注册使用@ServerEndpoint注解声明的websocket endpoint
*
* @return
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
第七步
新建schedule文件夹,在其下创建WeiBoCheckSchedule .java
package bx.springChart.demoProject.schedule;
import bx.springChart.demoProject.model.WeiBoNewModel;
import bx.springChart.demoProject.service.WebSocketService;
import bx.springChart.demoProject.socket.WebSocketServer;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
/**
* 警情定时器,通知websocket
* author:robert
*/
@Component
public class WeiBoCheckSchedule {
@Resource
private WebSocketService webSocketService;
private List<WeiBoNewModel> currentTimeList = null;
private WebSocketServer socket = new WebSocketServer();
/**
*
*/
@Scheduled(cron = "*/5 * * * * ?")//五秒轮询
public void TimeStampsSearch() {
try {
if (currentTimeList == null) {
currentTimeList = webSocketService.getAllTimeStamps();
} else {
List<WeiBoNewModel> newTimeList = webSocketService.getAllTimeStamps();
for (int i = 0; i < currentTimeList.size(); i++) {
if (currentTimeList.size()!=newTimeList.size()) {
System.out.println("list长度不相等");
System.out.println("数据更新了");
socket.sendInfo("10", "1");
break;
}
if (!currentTimeList.get(i).getOnboardTime().equals(newTimeList.get(i).getOnboardTime())) {
System.out.println("数据时间不一致");
System.out.println("数据更新了");
socket.sendInfo("10", "1");
break;
}
}
currentTimeList = newTimeList;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
第八步
在echartApplication.java中添加@EnableScheduling注解
第九步
将weiBoTopReDraw.html中87行的定时轮询方法注释
//设定每30秒执行一次ajax
//setInterval(function () {
// aJax();
// run();
//}, 30000);
添加以下script
<script>
let websocket = null;
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
//建立连接,这里的/websocket ,是ManagerServlet中开头注解中的那个值
websocket = new WebSocket("ws://localhost:8081/webSocket/10");
}
else {
alert('当前浏览器不支持websocket')
}
//连接发生错误的回调方法
websocket.onerror = function () {
setMessageInnerHTML("WebSocket连接发生错误");
};
//连接成功建立的回调方法
websocket.onopen = function () {
setMessageInnerHTML("WebSocket连接成功");
}
//接收到消息的回调方法
websocket.onmessage = function (event) {
setMessageInnerHTML(event.data);
if(event.data=="1"){
// location.reload();
aJax();
run();
//console.log("接到了响应,调用了AJAX");
}
}
//连接关闭的回调方法
websocket.onclose = function () {
setMessageInnerHTML("WebSocket连接关闭");
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
closeWebSocket();
}
//将消息显示在网页上
function setMessageInnerHTML(innerHTML) {
// document.getElementById('message').innerHTML += innerHTML + '<br/>';
console.log(innerHTML);
}
//关闭WebSocket连接
function closeWebSocket() {
websocket.close();
}
</script>
目录结构
如下
运行效果