本文档介绍了如何在前端项目中使用TR Spring Boot SSE插件提供的实时推送功能。通过Server-Sent Events (SSE)技术,客户端可以接收来自服务器的实时数据推送。
Server-Sent Events (SSE) 是一种服务器向浏览器推送实时数据的技术,基于HTTP协议,相比WebSocket更轻量,适用于单向数据推送场景。
使用JavaScript的EventSource API建立SSE连接:
// 创建EventSource实例
const eventSource = new EventSource('http://localhost:8080/sse/subscribe', {
withCredentials: true // 如需携带Cookie
});
// 监听连接打开事件
eventSource.onopen = function(event) {
console.log('SSE连接已建立');
};
// 监听连接错误事件
eventSource.onerror = function(event) {
console.error('SSE连接错误:', event);
};
通过向服务器发送订阅请求来订阅感兴趣的主题:
// 订阅主题
async function subscribeTopics(clientId, topics) {
try {
const response = await fetch('/sse/subscribe', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
clientId: clientId,
topics: topics
})
});
if (response.ok) {
console.log('主题订阅成功');
} else {
console.error('主题订阅失败');
}
} catch (error) {
console.error('订阅请求异常:', error);
}
}
// 使用示例
subscribeTopics('client-001', ['notification', 'system_alert']);
监听特定主题的事件:
// 监听notification主题事件
eventSource.addEventListener('notification', function(event) {
const data = JSON.parse(event.data);
console.log('收到通知:', data);
// 处理通知数据
handleNotification(data);
});
// 监听system_alert主题事件
eventSource.addEventListener('system_alert', function(event) {
const data = JSON.parse(event.data);
console.log('收到系统警报:', data);
// 处理系统警报数据
handleSystemAlert(data);
});
// 监听所有事件
eventSource.onmessage = function(event) {
console.log('收到事件:', event.type, event.data);
};
定期向服务器发送心跳以维持连接有效性:
// 发送心跳
async function sendHeartbeat(clientId) {
try {
const response = await fetch('/sse/ping', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
clientId: clientId
})
});
if (response.ok) {
const result = await response.json();
console.log('心跳发送成功:', result);
} else {
console.error('心跳发送失败');
}
} catch (error) {
console.error('心跳请求异常:', error);
}
}
// 每隔25秒发送一次心跳
const heartbeatInterval = setInterval(() => {
sendHeartbeat('client-001');
}, 25000);
在不需要接收推送时,主动取消订阅:
// 取消订阅
async function unsubscribe(clientId) {
try {
const response = await fetch('/sse/unsubscribe', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
clientId: clientId
})
});
if (response.ok) {
const result = await response.json();
console.log('取消订阅成功:', result);
} else {
console.error('取消订阅失败');
}
} catch (error) {
console.error('取消订阅请求异常:', error);
}
}
// 页面卸载时取消订阅
window.addEventListener('beforeunload', () => {
unsubscribe('client-001');
clearInterval(heartbeatInterval);
});
在适当的时候关闭SSE连接:
// 关闭SSE连接
function closeConnection() {
if (eventSource) {
eventSource.close();
console.log('SSE连接已关闭');
}
}
// 页面卸载时关闭连接
window.addEventListener('beforeunload', closeConnection);
服务器监控事件提供服务器的实时状态信息,包括CPU、内存、存储、网络等关键指标。
eventSource.addEventListener('server_monitor', function(event) {
const data = JSON.parse(event.data);
// 处理服务器监控数据
});
| 字段 | 类型 | 说明 |
|---|---|---|
devMonitorCpuInfo |
Object | CPU信息 |
devMonitorMemoryInfo |
Object | 内存信息 |
devMonitorStorageInfo |
Object | 存储信息 |
devMonitorNetworkInfo |
Object | 网络信息 |
devMonitorServerInfo |
Object | 服务器信息 |
devMonitorJvmInfo |
Object | JVM信息 |
| 字段 | 类型 | 说明 | 示例值 |
|---|---|---|---|
cupName |
String | CPU名称 | "Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz" |
cupNum |
String | CPU数量 | "1颗物理CPU" |
cpuPhysicalCoreNum |
String | CPU物理核心数 | "6个物理核心" |
cpuLogicalCoreNum |
String | CPU逻辑核心数 | "12个逻辑核心" |
cpuSysUseRate |
String | CPU系统使用率 | "2.5%" |
cpuUserUseRate |
String | CPU用户使用率 | "8.3%" |
cpuTotalUseRate |
Double | CPU当前总使用率 | 10.8 |
cpuWaitRate |
String | CPU当前等待率 | "0.1%" |
cpuFreeRate |
String | CPU当前空闲率 | "89.2%" |
| 字段 | 类型 | 说明 | 示例值 |
|---|---|---|---|
memoryTotal |
String | 内存总量 | "16.0 GB" |
memoryUsed |
String | 内存已用 | "8.2 GB" |
memoryFree |
String | 内存剩余 | "7.8 GB" |
memoryUseRate |
Double | 内存使用率 | 51.25 |
| 字段 | 类型 | 说明 | 示例值 |
|---|---|---|---|
storageTotal |
String | 存储总量 | "512.0 GB" |
storageUsed |
String | 存储已用 | "128.5 GB" |
storageFree |
String | 存储剩余 | "383.5 GB" |
storageUseRate |
Double | 存储使用率 | 25.1 |
| 字段 | 类型 | 说明 | 示例值 |
|---|---|---|---|
upLinkRate |
String | 上行速率 | "1.2 Mbps" |
downLinkRate |
String | 下行速率 | "15.6 Mbps" |
| 字段 | 类型 | 说明 | 示例值 |
|---|---|---|---|
serverName |
String | 服务器名称 | "DESKTOP-ABC123" |
serverOs |
String | 服务器操作系统 | "Windows 10" |
serverIp |
String | 服务器IP | "192.168.1.100" |
serverArchitecture |
String | 服务器架构 | "amd64" |
| 字段 | 类型 | 说明 | 示例值 |
|---|---|---|---|
jvmName |
String | JVM名称 | "OpenJDK 64-Bit Server VM" |
jvmVersion |
String | JVM版本 | "17.0.2" |
jvmMemoryTotal |
String | JVM总分配内存 | "2.0 GB" |
jvmMemoryUsed |
String | JVM已用内存 | "1.2 GB" |
jvmMemoryFree |
String | JVM剩余内存 | "0.8 GB" |
jvmUseRate |
Double | JVM内存使用率 | 60.0 |
jvmStartTime |
String | JVM启动时间 | "2023-01-01 10:00:00" |
jvmRunTime |
String | JVM运行时长 | "2天3小时15分钟" |
javaVersion |
String | Java版本 | "17.0.2" |
javaPath |
String | Java安装路径 | "/usr/lib/jvm/java-17-openjdk" |
{
"devMonitorCpuInfo": {
"cupName": "Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz",
"cupNum": "1颗物理CPU",
"cpuPhysicalCoreNum": "6个物理核心",
"cpuLogicalCoreNum": "12个逻辑核心",
"cpuSysUseRate": "2.5%",
"cpuUserUseRate": "8.3%",
"cpuTotalUseRate": 10.8,
"cpuWaitRate": "0.1%",
"cpuFreeRate": "89.2%"
},
"devMonitorMemoryInfo": {
"memoryTotal": "16.0 GB",
"memoryUsed": "8.2 GB",
"memoryFree": "7.8 GB",
"memoryUseRate": 51.25
},
"devMonitorStorageInfo": {
"storageTotal": "512.0 GB",
"storageUsed": "128.5 GB",
"storageFree": "383.5 GB",
"storageUseRate": 25.1
},
"devMonitorNetworkInfo": {
"upLinkRate": "1.2 Mbps",
"downLinkRate": "15.6 Mbps"
},
"devMonitorServerInfo": {
"serverName": "DESKTOP-ABC123",
"serverOs": "Windows 10",
"serverIp": "192.168.1.100",
"serverArchitecture": "amd64"
},
"devMonitorJvmInfo": {
"jvmName": "OpenJDK 64-Bit Server VM",
"jvmVersion": "17.0.2",
"jvmMemoryTotal": "2.0 GB",
"jvmMemoryUsed": "1.2 GB",
"jvmMemoryFree": "0.8 GB",
"jvmUseRate": 60.0,
"jvmStartTime": "2023-01-01 10:00:00",
"jvmRunTime": "2天3小时15分钟",
"javaVersion": "17.0.2",
"javaPath": "/usr/lib/jvm/java-17-openjdk"
}
}
class SSEClient {
constructor(baseUrl, clientId) {
this.baseUrl = baseUrl;
this.clientId = clientId;
this.eventSource = null;
this.heartbeatInterval = null;
}
// 建立连接并订阅主题
async connect(topics) {
try {
// 发送订阅请求
const response = await fetch(`${this.baseUrl}/sse/subscribe`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
clientId: this.clientId,
topics: topics
})
});
if (response.ok) {
// 建立SSE连接
this.eventSource = new EventSource(`${this.baseUrl}/sse/subscribe`);
// 设置事件监听器
this.setupEventListeners();
// 启动心跳
this.startHeartbeat();
console.log('SSE连接建立成功');
} else {
console.error('订阅失败');
}
} catch (error) {
console.error('连接异常:', error);
}
}
// 设置事件监听器
setupEventListeners() {
this.eventSource.onopen = (event) => {
console.log('SSE连接已打开');
};
this.eventSource.onerror = (event) => {
console.error('SSE连接错误:', event);
};
// 监听自定义事件
this.eventSource.addEventListener('notification', (event) => {
const data = JSON.parse(event.data);
this.handleNotification(data);
});
this.eventSource.addEventListener('system_alert', (event) => {
const data = JSON.parse(event.data);
this.handleSystemAlert(data);
});
// 监听服务器监控事件
this.eventSource.addEventListener('server_monitor', (event) => {
const data = JSON.parse(event.data);
this.handleServerMonitor(data);
});
}
// 处理通知事件
handleNotification(data) {
console.log('收到通知:', data);
// 在这里处理通知数据,例如更新UI
}
// 处理系统警报事件
handleSystemAlert(data) {
console.log('收到系统警报:', data);
// 在这里处理系统警报数据,例如显示警告框
}
// 处理服务器监控事件
handleServerMonitor(data) {
console.log('收到服务器监控数据:', data);
// 在这里处理监控数据,例如更新监控面板
// 更新CPU使用率显示
const cpuUsage = data.devMonitorCpuInfo.cpuTotalUseRate;
document.getElementById('cpu-usage').textContent = cpuUsage + '%';
// 更新内存使用率显示
const memoryUsage = data.devMonitorMemoryInfo.memoryUseRate;
document.getElementById('memory-usage').textContent = memoryUsage + '%';
// 更新存储使用率显示
const storageUsage = data.devMonitorStorageInfo.storageUseRate;
document.getElementById('storage-usage').textContent = storageUsage + '%';
}
// 启动心跳
startHeartbeat() {
this.heartbeatInterval = setInterval(() => {
this.sendHeartbeat();
}, 25000); // 每25秒发送一次心跳
}
// 发送心跳
async sendHeartbeat() {
try {
await fetch(`${this.baseUrl}/sse/ping`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
clientId: this.clientId
})
});
} catch (error) {
console.error('心跳发送失败:', error);
}
}
// 断开连接
disconnect() {
// 停止心跳
if (this.heartbeatInterval) {
clearInterval(this.heartbeatInterval);
this.heartbeatInterval = null;
}
// 关闭SSE连接
if (this.eventSource) {
this.eventSource.close();
this.eventSource = null;
}
// 发送取消订阅请求
this.unsubscribe();
console.log('SSE连接已断开');
}
// 取消订阅
async unsubscribe() {
try {
await fetch(`${this.baseUrl}/sse/unsubscribe`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
clientId: this.clientId
})
});
} catch (error) {
console.error('取消订阅失败:', error);
}
}
}
// 使用示例
const sseClient = new SSEClient('http://localhost:8080', 'client-001');
sseClient.connect(['notification', 'system_alert', 'server_monitor']);
// 页面卸载时断开连接
window.addEventListener('beforeunload', () => {
sseClient.disconnect();
});
在使用SSE时需要注意以下错误情况:
跨域问题: 确保服务器正确配置CORS
// 错误处理示例
eventSource.onerror = function(event) {
if (event.target.readyState === EventSource.CLOSED) {
console.log('SSE连接已关闭');
} else if (event.target.readyState === EventSource.CONNECTING) {
console.log('SSE连接正在重试...');
} else {
console.error('SSE连接发生未知错误');
}
};
SSE在现代浏览器中得到良好支持:
对于不支持SSE的旧浏览器,可以考虑使用polyfill或者降级到长轮询方案。