一、maven 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
二、启用WebSocket
package com.itunion.example;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* @author qiao
* @version 2020-12-25
*/
@Configuration
public class WebSocketConfig {
/**
* ServerEndpointExporter 作用
*
* 这个Bean会自动注册使用@ServerEndpoint注解声明的websocket endpoint
*
* @return
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
三、开放接口
package com.itunion.example.web;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.annotation.PostConstruct;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author qiao
* @version 2020-12-25
*/
@Slf4j
@ServerEndpoint("/api/monitor/socket")
@Controller
@RequestMapping("/monitor")
public class Monitors {
@GetMapping
public String index(){
return "monitor";
}
@PostConstruct
public void init(){
new Thread(() -> {
// 模拟监控信息
while (true) {
try {
sendMessage("获取到的监控信息...");
Thread.sleep(new Random().nextInt(5000));
} catch (InterruptedException e) {
break;
}
}
}).start();
}
//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static AtomicInteger onlineNum = new AtomicInteger();
//concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketServer对象。
private static ConcurrentHashMap<String, Session> sessionPools = new ConcurrentHashMap<>();
//异步广播内容
public void sendMessage(Session session, String content) {
session.getAsyncRemote().sendText(content);
}
//异步广播内容
public void sendMessage(String content) {
final Set<Map.Entry<String, Session>> entries = sessionPools.entrySet();
for (Map.Entry<String, Session> entry : entries) {
final Session session = entry.getValue();
session.getAsyncRemote().sendText(content);
}
}
//建立连接成功调用
@OnOpen
public void onOpen(Session session) {
sessionPools.put(session.getId(), session);
addOnlineCount();
sendMessage(session, "开始获取监控信息");
}
//关闭连接时调用
@OnClose
public void onClose(Session session) {
sessionPools.remove(session.getId());
subOnlineCount();
}
//错误时调用
@OnError
public void onError(Session session, Throwable throwable) {
sessionPools.remove(session.getId());
log.info("onError: {}", session.getId(), throwable);
}
private static void addOnlineCount() {
final int val = onlineNum.incrementAndGet();
log.info("当前人数为:{}", val);
}
private static void subOnlineCount() {
final int val = onlineNum.decrementAndGet();
log.info("当前人数为:{}", val);
}
}
四、前端连接
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>监控页面</title>
</head>
<body>
<textarea id="content" rows="10" style="width: 100%" readonly></textarea>
<script>
function openSocket() {
if(typeof(WebSocket) == "undefined") {
alert("您的浏览器不支持WebSocket");
}else{
console.log("connect...");
//实现化WebSocket对象,指定要连接的服务器地址与端口 建立连接
var socket = new WebSocket(socketUrl);
//打开事件
socket.onopen = function() {
console.log("websocket已打开");
};
//获得消息事件
socket.onmessage = function(event) {
console.log(event);
log(event.data);
};
//关闭事件
socket.onclose = function() {
log("close");
};
//发生了错误事件
socket.onerror = function () {
log("error");
};
return socket;
}
}
function log(msg) {
var ele = document.getElementById("content");
ele.innerHTML += msg + "\n";
ele.scrollTop = ele.scrollHeight;
}
openSocket();
</script>
</body>
</html>
五、启动服务访问页面