2019独角兽企业重金招聘Python工程师标准>>>
- 背景:
最近涉及消息通知功能,在管理员创建发送消息时,登陆用户可以实时接收到新增消息的提醒,避免频繁刷新,通过websocket取代轮询setInterval。
- Spring boot部分:
- POM.xml
org.springframework.boot
spring-boot-starter-websocket
- 新建配置类:
package io.xcc.config;
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
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
- 新建实现类:
package io.xcc.common.socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import org.springframework.stereotype.Component;
@Component
@ServerEndpoint("/websocket/{userName}")
//此注解相当于设置访问URL
public class WebSocket {
private Session session;
private static CopyOnWriteArraySet webSockets =new CopyOnWriteArraySet<>();
private static Map sessionPool = new HashMap();
@OnOpen
public void onOpen(Session session, @PathParam(value="userName")String userName) {
this.session = session;
webSockets.add(this);
sessionPool.put(userName, session);
System.out.println(userName+"【websocket消息】有新的连接,总数为:"+webSockets.size());
}
@OnClose
public void onClose() {
webSockets.remove(this);
System.out.println("【websocket消息】连接断开,总数为:"+webSockets.size());
}
@OnMessage
public void onMessage(String message) {
System.out.println("【websocket消息】收到客户端消息:"+message);
}
// 此为广播消息
public void sendAllMessage(String message) {
for(WebSocket webSocket : webSockets) {
System.out.println("【websocket消息】广播消息:"+message);
try {
webSocket.session.getAsyncRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 此为单点消息
public void sendOneMessage(String userName, String message) {
System.out.println("【websocket消息】单点消息:"+message);
Session session = sessionPool.get(userName);
if (session != null) {
try {
session.getAsyncRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
- 新增调用类:
@GetMapping("/sendAllWebSocket")
public String test() {
String text="你们好!这是websocket群体发送!";
webSocket.sendAllMessage(text);
return text;
}
@GetMapping("/sendOneWebSocket/{userName}")
public String sendOneWebSocket(@PathVariable("userName") String userName) {
String text=userName+" 你好! 这是websocket单人发送!";
webSocket.sendOneMessage(userName,text);
return text;
}
- VUE部分:
mounted () {
// WebSocket
if ('WebSocket' in window) {
this.websocket = new WebSocket('ws://localhost:8801/myproject/websocket/' + this.userName)
this.initWebSocket()
} else {
alert('当前浏览器 Not support websocket')
}
},
beforeDestroy () {
this.onbeforeunload()
},
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 () {
console.log('WebSocket连接发生错误 状态码:' + this.websocket.readyState)
},
setOnopenMessage () {
console.log('WebSocket连接成功 状态码:' + this.websocket.readyState)
},
setOnmessageMessage (event) {
// 根据服务器推送的消息做自己的业务处理
console.log('服务端返回:' + event.data)
},
setOncloseMessage () {
console.log('WebSocket连接关闭 状态码:' + this.websocket.readyState)
},
onbeforeunload () {
this.closeWebSocket()
},
closeWebSocket () {
this.websocket.close()
}
}
- 测试效果:
http://localhost:8801/myproject/app/sendOneWebSocket/admin
http://localhost:8801/myproject/app/sendOneWebSocket/czm
http://localhost:8801/myproject/app/sendAllWebSocket
用两个浏览器分别登陆两个账号,控制台结果如下图:
【图1】
【图2】
- 参考主要来源:
https://segmentfault.com/a/1190000017268973
- 其他注意事项:
- 不要试图通过单元测试测试,因为session不同获取不到;
- 设置完前台没有显示链接成功,请注意修改自己的过滤器;