Springboot Vue WebSocket实现后端服务推送通知功能
一、业务场景
最近在做一个功能,业务待办事件数量实时推送给前端页面,最初的设想是在前端定时调用后端接口获取,但是这样就无法实现实时信息,所以,就想到了WebSocket。废话不多说,上菜。。。
二、实现过程
Springboot 和 Vue项目的构建我就不赘述了,老生常谈。
1. Springboot pom.xml引入WebSocket依赖jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
2. 编写WebSocketConfig配置类
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
3. 编写WebSocketServer服务实现类
@Component
@ServerEndpoint("/push/websocket")
@Slf4j
@EqualsAndHashCode
public class WebSocketServer {
/**
* 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
*/
private static int onlineCount = 0;
/**
* concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
*/
private final static CopyOnWriteArraySet<WebSocketServer> WEB_SOCKET_SET = new CopyOnWriteArraySet<>();
/**
* 与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
private Session session;
/**
* 接收sid
*/
private String sid = "";
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("sid") String sid) throws IOException {
this.session = session;
// 加入set中
WEB_SOCKET_SET.add(this);
// 在线数加1
addOnlineCount();
log.info("有新客户端连接:" + sid + ",当前在线人数为" + getOnlineCount());
this.sid = sid;
// TODO 简历连接成功后可以直接发送消息
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
//从set中删除
WEB_SOCKET_SET.remove(this);
// 在线数减1
subOnlineCount();
log.info("有一个客户端连接关闭!当前在线人数为" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("收到来自窗口" + sid + "的信息:" + message);
System.out.println(session);
}
/**
* @param session session
* @param e 异常信息
*/
@OnError
public void onError(Session session, Throwable e) {
log.error("发生错误");
System.out.println(session);
e.printStackTrace();
}
/**
* 实现服务器主动推送
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
/**
* 群发自定义消息
*/
public void sendInfo(String message) throws IOException {
for (WebSocketServer item : WEB_SOCKET_SET) {
item.sendMessage(message);
log.info("推送信息内容=>{}", message);
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
}
4. 编写一个Controller接口用于调用(真正业务上的调用方式应该是数据改变时调用发送消息,这里用于测试)
@Slf4j
@RestController
@RequestMapping("/websocket")
public class WebSocketController {
private final WebSocketServer webSocketServer;
public WebSocketController (WebSocketServer webSocketServer) {
this.webSocketServer= webSocketServer;
}
@GetMapping("test")
public void test() {
try {
webSocketServer.sendInfo("后端服务推送信息");
} catch (Exception e) {
log.error("分页查询数据接口列表失败", e);
}
}
}
5. 编写Vue前端代码配置
export default {
name: "Asider",
data() {
return {
websocket: null
}
},
methods: {
initWebSocket() {
// 连接错误
this.websocket.onerror = this.setErrorMessage
// 连接成功
this.websocket.onopen = this.setOnopenMessage
// 收到消息的回调
this.websocket.onmessage = this.setOnmessageMessage
// 连接关闭的回调
this.websocket.onclose = this.setOncloseMessage
// 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = this.onbeforeunload
},
setErrorMessage() {
window.console.log('WebSocket连接发生错误,状态码:' + this.websocket.readyState)
},
setOnopenMessage() {
window.console.log('WebSocket连接成功,状态码:' + this.websocket.readyState)
},
setOnmessageMessage(event) {
// 根据服务器推送的消息做自己的业务处理
window.console.log(event.data);
},
setOncloseMessage() {
window.console.log('WebSocket连接关闭,状态码:' + this.websocket.readyState)
},
onbeforeunload() {
this.closeWebSocket()
},
closeWebSocket() {
this.websocket.close()
}
},
mounted() {
if ('WebSocket' in window) {
this.websocket = new WebSocket('ws://localhost:8080/push/websocket');
this.initWebSocket();
} else {
alert('当前浏览器不支持WebSocket!!!')
}
}
}
三、结语
编写完成,欢迎一键三连。