FRONTEND_GUIDE.md 15 KB

前端SSE对接指南

概述

本文档介绍了如何在前端项目中使用TR Spring Boot SSE插件提供的实时推送功能。通过Server-Sent Events (SSE)技术,客户端可以接收来自服务器的实时数据推送。

基础知识

Server-Sent Events (SSE) 是一种服务器向浏览器推送实时数据的技术,基于HTTP协议,相比WebSocket更轻量,适用于单向数据推送场景。

前端实现

1. 建立SSE连接

使用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);
};

2. 订阅主题

通过向服务器发送订阅请求来订阅感兴趣的主题:

// 订阅主题
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']);

3. 接收事件

监听特定主题的事件:

// 监听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);
};

4. 发送心跳

定期向服务器发送心跳以维持连接有效性:

// 发送心跳
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);

5. 取消订阅

在不需要接收推送时,主动取消订阅:

// 取消订阅
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);
});

6. 关闭连接

在适当的时候关闭SSE连接:

// 关闭SSE连接
function closeConnection() {
    if (eventSource) {
        eventSource.close();
        console.log('SSE连接已关闭');
    }
}

// 页面卸载时关闭连接
window.addEventListener('beforeunload', closeConnection);

支持的事件类型

服务器监控事件 (server_monitor)

服务器监控事件提供服务器的实时状态信息,包括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信息
CPU信息 (devMonitorCpuInfo)
字段 类型 说明 示例值
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%"
内存信息 (devMonitorMemoryInfo)
字段 类型 说明 示例值
memoryTotal String 内存总量 "16.0 GB"
memoryUsed String 内存已用 "8.2 GB"
memoryFree String 内存剩余 "7.8 GB"
memoryUseRate Double 内存使用率 51.25
存储信息 (devMonitorStorageInfo)
字段 类型 说明 示例值
storageTotal String 存储总量 "512.0 GB"
storageUsed String 存储已用 "128.5 GB"
storageFree String 存储剩余 "383.5 GB"
storageUseRate Double 存储使用率 25.1
网络信息 (devMonitorNetworkInfo)
字段 类型 说明 示例值
upLinkRate String 上行速率 "1.2 Mbps"
downLinkRate String 下行速率 "15.6 Mbps"
服务器信息 (devMonitorServerInfo)
字段 类型 说明 示例值
serverName String 服务器名称 "DESKTOP-ABC123"
serverOs String 服务器操作系统 "Windows 10"
serverIp String 服务器IP "192.168.1.100"
serverArchitecture String 服务器架构 "amd64"
JVM信息 (devMonitorJvmInfo)
字段 类型 说明 示例值
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时需要注意以下错误情况:

  1. 网络异常: EventSource会自动尝试重连
  2. 服务器错误: 需要检查HTTP状态码
  3. 连接超时: 通过心跳机制维持连接
  4. 跨域问题: 确保服务器正确配置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连接发生未知错误');
    }
    };
    

最佳实践

  1. 客户端ID管理: 使用唯一标识符作为clientId
  2. 心跳机制: 定期发送心跳维持连接
  3. 资源清理: 页面卸载时及时关闭连接
  4. 错误重试: 实现适当的重试机制
  5. 数据解析: 正确解析服务器推送的JSON数据
  6. UI更新: 在主线程外处理大量数据更新

浏览器兼容性

SSE在现代浏览器中得到良好支持:

  • Chrome 6+
  • Firefox 6+
  • Safari 5+
  • Edge 79+

对于不支持SSE的旧浏览器,可以考虑使用polyfill或者降级到长轮询方案。

性能优化

  1. 减少连接数: 合理复用SSE连接
  2. 事件过滤: 只订阅需要的主题
  3. 数据压缩: 服务端可以启用GZIP压缩
  4. 批量处理: 对于高频事件,考虑批量推送