实时通信模块
youlai-boot 基于 WebSocket + STOMP 协议实现实时通信功能,支持服务端主动推送消息到前端,适用于用户在线状态管理、字典变更通知等场景。
设计目标
| 目标 | 说明 |
|---|---|
| 实时推送 | 服务端数据变更后立即通知前端,无需轮询 |
| 按需刷新 | 通知前端清除缓存,下次使用时再加载,避免缓存穿透 |
| 多设备支持 | 同一用户支持多设备同时在线 |
| 安全认证 | 基于 JWT 的 WebSocket 连接认证 |
| 断线重连 | 前端自动重连与订阅恢复 |
应用场景
| 场景 | 说明 | 通知方式 |
|---|---|---|
| 用户在线状态 | 实时统计在线用户数,支持多设备登录 | 广播 /topic/online-count |
| 字典变更通知 | 字典数据变更后通知前端清除缓存 | 广播 /topic/dict |
整体架构
核心组件
| 组件 | 说明 |
|---|---|
WebSocketConfig | STOMP 端点配置,消息代理设置 |
WebSocketServiceImpl | 核心服务,用户在线管理与消息推送 |
UserSessionRegistry | 用户会话注册表,维护在线状态 |
WebSocketPublisher | 消息发布器,封装 STOMP 消息发送 |
DictChangeEvent | 字典变更事件 DTO |
连接与认证流程
用户在线状态管理
数据结构
采用双 Map 结构实现高效查询:
| 映射表 | Key | Value | 用途 |
|---|---|---|---|
userSessionsMap | 用户名 | 会话ID集合 | 支持多设备登录,统计用户会话数 |
sessionDetailsMap | 会话ID | 会话详情 | 快速定位用户,获取连接时间 |
在线用户统计
java
// 在线用户数(非会话数)
public int getOnlineUserCount() {
return userSessionsMap.size();
}
// 在线会话总数(包含多设备)
public int getTotalSessionCount() {
return sessionDetailsMap.size();
}多设备登录处理
字典变更通知
设计思路
采用"通知清除,按需加载"策略,避免推送完整字典数据:
优势分析
| 策略 | 实时性 | 带宽消耗 | 缓存一致性 | 适用场景 |
|---|---|---|---|---|
| 推送完整数据 | 高 | 高 | 强 | 数据量小 |
| 推送变更数据 | 高 | 中 | 中 | 需增量更新 |
| 通知清除(本项目) | 高 | 低 | 强 | 通用场景 |
| 过期刷新 | 低 | 低 | 弱 | 数据实时性要求低 |
消息结构
后端 DTO
java
@Data
public class DictChangeEvent implements Serializable {
/** 字典编码 */
private String dictCode;
/** 事件时间戳 */
private long timestamp;
}前端接口
typescript
interface DictChangeMessage {
/** 字典编码 */
dictCode: string;
/** 时间戳 */
timestamp: number;
}字典变更流程
消息主题定义
| 主题 | 类型 | 说明 | 消息体 |
|---|---|---|---|
/topic/dict | 广播 | 字典变更通知 | DictChangeEvent |
/topic/online-count | 广播 | 在线用户数变更 | Integer |
/topic/public | 广播 | 系统公共消息 | TextMessage |
/user/queue/messages | 点对点 | 用户个人消息 | Object |
前端集成
连接管理
typescript
// useStomp.ts - STOMP 连接管理
const stomp = useStomp({
reconnectDelay: 15000, // 重连延迟
connectionTimeout: 10000, // 连接超时
maxReconnectAttempts: 3, // 最大重连次数
autoRestoreSubscriptions: true, // 自动恢复订阅
});字典同步
typescript
// useDictSync.ts - 字典同步
const dictSync = useDictSync();
// 初始化(应用启动时)
dictSync.initialize();
// 注册回调
dictSync.onDictChange((message) => {
console.log('字典已更新:', message.dictCode);
});核心特性
| 特性 | 说明 |
|---|---|
| 自动重连 | 断线后自动重连,支持指数退避 |
| 订阅恢复 | 重连后自动恢复之前的订阅 |
| 标签页可见性 | 标签页激活时检查连接状态 |
| Token 自动刷新 | 连接时自动获取最新 Token |
配置项
后端配置
yaml
# WebSocket 端点配置(前端环境变量)
VITE_APP_WS_ENDPOINT=ws://localhost:8080/ws安全配置
yaml
security:
# WebSocket 端点允许匿名访问(握手时校验 Token)
ignore-urls:
- /ws/**定时任务
系统通过定时任务定期广播在线用户数,确保数据一致性:
java
@Scheduled(fixedRate = 60000) // 每分钟
public void broadcastOnlineCount() {
webSocketService.notifyOnlineUsersChange();
}最佳实践
后端
- 参数校验:所有公开方法进行参数校验
- 线程安全:使用
ConcurrentHashMap保证线程安全 - 日志记录:连接/断开/消息推送记录日志
- 资源清理:用户断开时及时清理会话
前端
- 单例模式:WebSocket 连接使用单例,避免重复连接
- 按需订阅:只订阅必要的主题
- 错误处理:消息解析失败时记录日志,不影响主流程
- 缓存策略:收到通知后清除缓存,下次使用时再加载
相关文件
| 文件路径 | 说明 |
|---|---|
platform/websocket/config/WebSocketConfig.java | WebSocket 配置类 |
platform/websocket/service/WebSocketService.java | 服务接口 |
platform/websocket/service/impl/WebSocketServiceImpl.java | 服务实现 |
platform/websocket/session/UserSessionRegistry.java | 会话注册表 |
platform/websocket/publisher/WebSocketPublisher.java | 消息发布器 |
platform/websocket/dto/DictChangeEvent.java | 字典变更事件 |
platform/websocket/dto/OnlineUserDTO.java | 在线用户 DTO |
platform/websocket/topic/WebSocketTopics.java | 主题常量定义 |
vue3-element-admin/src/composables/websocket/useStomp.ts | STOMP 连接管理 |
vue3-element-admin/src/composables/websocket/useDictSync.ts | 字典同步 |
