RuoYi-Vue-fast集成WebSocket:实时通信功能实现
你是否在开发后台管理系统时遇到过这些困扰?管理员修改配置后用户页面无法实时更新,重要通知需要用户手动刷新才能看到,在线聊天功能实现复杂?本文将详细介绍如何在RuoYi-Vue-fast框架中集成WebSocket(套接字),通过5个步骤快速实现实时通信功能,让你的系统具备即时消息推送、在线状态同步等能力。
一、WebSocket简介与环境准备
WebSocket是一种在单个TCP连接上进行全双工通信的协议,它允许服务器主动向客户端推送数据,非常适合实时通知、在线聊天、实时监控等场景。与传统的HTTP轮询相比,WebSocket具有更低的延迟和更高的效率。
在开始集成前,请确保你的开发环境满足以下要求:
- JDK 1.8或更高版本
- Maven 3.0+构建工具
- RuoYi-Vue-fast框架基础环境
项目核心配置文件参考:
- 项目依赖配置:pom.xml
- 应用启动类:src/main/java/com/ruoyi/RuoYiApplication.java
二、添加WebSocket依赖
首先需要在项目的pom.xml文件中添加WebSocket相关依赖。由于RuoYi-Vue-fast使用Spring Boot框架,我们可以直接使用Spring提供的WebSocket starter。
打开pom.xml文件,在<dependencies>节点下添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
添加位置建议放在Spring Boot核心依赖区域,与其他starter保持一致,例如放在spring-boot-starter-security依赖之后。
三、配置WebSocket服务器
接下来需要创建WebSocket配置类,注册WebSocket端点并配置相关参数。
在src/main/java/com/ruoyi/framework/config/目录下创建WebSocketConfig.java文件,内容如下:
package com.ruoyi.framework.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.HandshakeInterceptor;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
import com.ruoyi.framework.interceptor.WebSocketHandshakeInterceptor;
@Configuration
public class WebSocketConfig {
/**
* 注册WebSocket端点处理器
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
/**
* 注册WebSocket握手拦截器
*/
@Bean
public HandshakeInterceptor handshakeInterceptor() {
return new WebSocketHandshakeInterceptor();
}
}
四、实现WebSocket服务端点
创建WebSocket服务类,处理连接建立、消息接收、连接关闭等事件。在src/main/java/com/ruoyi/project/web/controller/目录下创建WebSocketController.java文件:
package com.ruoyi.project.web.controller;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import org.springframework.stereotype.Component;
import com.ruoyi.common.utils.LogUtils;
import com.ruoyi.common.utils.StringUtils;
/**
* WebSocket服务端点
* 注意:@ServerEndpoint注解中的value属性定义了WebSocket连接的URL路径
*/
@ServerEndpoint(value = "/ws/message")
@Component
public class WebSocketController {
// 存储当前在线连接
private static ConcurrentHashMap<String, Session> onlineSessions = new ConcurrentHashMap<>();
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session) {
String userId = getUserIdFromSession(session);
if (StringUtils.isNotEmpty(userId)) {
onlineSessions.put(userId, session);
LogUtils.info("用户{}建立WebSocket连接,当前在线人数:{}", userId, onlineSessions.size());
}
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose(Session session) {
String userId = getUserIdFromSession(session);
if (StringUtils.isNotEmpty(userId)) {
onlineSessions.remove(userId);
LogUtils.info("用户{}关闭WebSocket连接,当前在线人数:{}", userId, onlineSessions.size());
}
}
/**
* 收到客户端消息后调用的方法
*/
@OnMessage
public void onMessage(String message, Session session) {
String userId = getUserIdFromSession(session);
LogUtils.info("收到用户{}的消息:{}", userId, message);
// 可以在这里处理消息,如广播给其他用户或存储消息等
}
/**
* 发生错误时调用
*/
@OnError
public void onError(Session session, Throwable error) {
String userId = getUserIdFromSession(session);
LogUtils.error("用户{}WebSocket连接发生错误:{}", userId, error.getMessage());
error.printStackTrace();
}
/**
* 从Session中获取用户ID
*/
private String getUserIdFromSession(Session session) {
// 实际项目中需要根据你的认证方式从Session中获取用户ID
// 这里仅作示例,实际实现需结合你的登录认证机制
return session.getId();
}
/**
* 发送消息给指定用户
*/
public static void sendMessageToUser(String userId, String message) throws IOException {
Session session = onlineSessions.get(userId);
if (session != null && session.isOpen()) {
session.getBasicRemote().sendText(message);
}
}
/**
* 广播消息给所有在线用户
*/
public static void broadcastMessage(String message) throws IOException {
for (Session session : onlineSessions.values()) {
if (session.isOpen()) {
session.getBasicRemote().sendText(message);
}
}
}
}
五、配置安全拦截器与握手处理
为了确保WebSocket连接的安全性,需要创建握手拦截器,验证用户身份。在src/main/java/com/ruoyi/framework/interceptor/目录下创建WebSocketHandshakeInterceptor.java文件:
package com.ruoyi.framework.interceptor;
import java.util.Map;
import javax.servlet.http.HttpSession;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.security.LoginUser;
public class WebSocketHandshakeInterceptor implements HandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpSession session = servletRequest.getServletRequest().getSession(false);
// 获取当前登录用户
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser == null) {
// 用户未登录,拒绝握手
return false;
}
// 将用户信息存入attributes,供WebSocketController使用
attributes.put("userId", loginUser.getUserId());
attributes.put("username", loginUser.getUsername());
}
return true;
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Exception exception) {
// 握手完成后的处理
}
}
同时,需要修改Spring Security配置,允许WebSocket连接的URL访问。打开安全配置类src/main/java/com/ruoyi/framework/config/SecurityConfig.java,在configure方法中添加WebSocket相关URL的放行规则:
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
// ... 其他配置
.authorizeRequests()
// 放行WebSocket连接
.antMatchers("/ws/**").permitAll()
// ... 其他URL配置
}
六、集成Redis实现分布式WebSocket
在分布式系统中,单个WebSocket服务实例无法处理所有节点的连接,需要使用Redis的发布订阅功能实现跨节点的消息广播。
首先在Redis配置类src/main/java/com/ruoyi/framework/config/RedisConfig.java中添加Redis消息监听容器配置:
@Bean
public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
// 可以添加多个消息监听器
return container;
}
然后创建Redis消息监听器,用于接收其他节点发送的消息并广播给当前节点的WebSocket连接:
@Component
public class WebSocketRedisListener extends KeyExpirationEventMessageListener {
public WebSocketRedisListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
@Override
public void onMessage(Message message, byte[] pattern) {
String channel = new String(message.getChannel(), StandardCharsets.UTF_8);
String messageBody = new String(message.getBody(), StandardCharsets.UTF_8);
if ("webSocket:broadcast".equals(channel)) {
try {
// 将消息广播给当前节点的所有WebSocket连接
WebSocketController.broadcastMessage(messageBody);
} catch (IOException e) {
LogUtils.error("广播WebSocket消息失败:{}", e.getMessage());
}
}
}
}
七、前端Vue集成WebSocket
在Vue前端项目中创建WebSocket服务,实现与后端的连接和消息处理。创建src/utils/websocket.js文件:
import store from '@/store'
let websocket = null
/**
* 初始化WebSocket连接
*/
export function initWebSocket() {
if (typeof (WebSocket) === 'undefined') {
console.error('您的浏览器不支持WebSocket')
return
}
// 获取当前用户token,用于认证
const token = store.getters.token
if (!token) {
console.error('用户未登录,无法建立WebSocket连接')
return
}
// WebSocket连接地址,根据实际情况修改
const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
const wsPath = `${wsProtocol}//${window.location.host}/ws/message?token=${token}`
// 创建WebSocket实例
websocket = new WebSocket(wsPath)
// 连接成功事件
websocket.onopen = function() {
console.log('WebSocket连接成功')
// 可以在这里发送初始化消息,如用户上线通知等
}
// 收到消息事件
websocket.onmessage = function(e) {
console.log('收到WebSocket消息:', e.data)
const message = JSON.parse(e.data)
// 处理消息,如显示通知、更新数据等
handleWebSocketMessage(message)
}
// 连接关闭事件
websocket.onclose = function() {
console.log('WebSocket连接关闭')
// 连接关闭后可以尝试重连
setTimeout(() => {
initWebSocket()
}, 3000)
}
// 连接错误事件
websocket.onerror = function() {
console.error('WebSocket连接发生错误')
}
// 监听窗口关闭事件,主动关闭WebSocket连接
window.onbeforeunload = function() {
closeWebSocket()
}
}
/**
* 关闭WebSocket连接
*/
export function closeWebSocket() {
if (websocket) {
websocket.close()
}
}
/**
* 发送WebSocket消息
*/
export function sendWebSocketMessage(message) {
if (websocket && websocket.readyState === WebSocket.OPEN) {
websocket.send(JSON.stringify(message))
} else {
console.error('WebSocket连接未建立或已关闭')
}
}
/**
* 处理WebSocket消息
*/
function handleWebSocketMessage(message) {
switch (message.type) {
case 'NOTIFICATION':
// 处理通知消息
store.dispatch('app/addNotification', message.data)
break
case 'SYSTEM_CONFIG':
// 处理系统配置更新消息
store.dispatch('settings/updateSystemConfig', message.data)
break
// 其他类型消息处理...
default:
console.log('未知类型的WebSocket消息:', message)
}
}
八、测试与验证
完成上述配置后,启动应用程序,使用WebSocket测试工具(如wscat)连接到WebSocket服务端点进行测试:
# 安装wscat
npm install -g wscat
# 连接到WebSocket服务
wscat -c ws://localhost:8080/ws/message
发送消息测试:
{"type":"MESSAGE","content":"Hello, WebSocket!"}
在实际项目中,你可以结合业务需求,实现如实时通知、在线聊天、实时监控等功能。例如,在系统管理模块中添加消息推送功能,当有新的系统公告发布时,通过WebSocket实时推送给所有在线用户。
系统通知相关代码参考:
- 通知实体类:src/main/java/com/ruoyi/project/system/domain/SysNotice.java
- 通知服务实现:src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java
九、常见问题与解决方案
-
连接失败问题:检查WebSocket端点URL是否正确,防火墙是否阻止了WebSocket端口,以及用户认证是否通过。相关配置可参考src/main/java/com/ruoyi/framework/security/config/SecurityConfig.java。
-
消息发送不出去:确认WebSocket连接状态是否为OPEN,可在发送前检查
websocket.readyState属性。 -
分布式环境下消息不同步:确保Redis服务正常运行,检查Redis消息监听器是否正确配置。相关代码参考src/main/java/com/ruoyi/framework/config/RedisConfig.java。
-
大量连接时性能问题:可考虑使用Nginx作为WebSocket反向代理,配置适当的连接超时时间,并对WebSocket连接进行负载均衡。
十、总结与扩展
通过本文介绍的方法,我们成功在RuoYi-Vue-fast框架中集成了WebSocket功能,实现了服务器与客户端的实时通信。你可以基于此扩展更多实用功能:
- 在线用户列表与状态显示
- 实时数据监控面板
- 即时通讯系统
- 协同编辑功能
- 实时日志输出
项目完整文档参考:doc/若依环境使用手册.docx,更多高级功能实现可参考官方文档和社区教程。
希望本文能帮助你快速掌握WebSocket在RuoYi-Vue-fast框架中的应用,为你的项目增添实时通信能力。如有任何问题或建议,欢迎在项目社区中交流讨论。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



