Spring WebSocket无法监听SessionDisconnectEvent问题求助
Spring WebSocket 前后端分离场景下无法监听SessionDisconnectEvent
我参考Baeldung的WebSocket Spring项目实现了Demo,编写了WebSocketListener组件监听WebSocket相关事件。在非前后端分离环境中,打开/关闭连接或页面时能正常捕获所有事件,但切换到前后端分离架构后,仅能监听到SessionConnectEvent,SessionDisconnectEvent完全无法触发。
后端Listener代码
@Component @Slf4j public class WebSocketListener { @EventListener public void handleWebSocketConnectListener(SessionConnectEvent sessionConnectEvent) { StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(sessionConnectEvent.getMessage()); log.debug("connect, sessionId = {}", headerAccessor.getSessionId()); } @EventListener public void handleWebSocketConnectedListener(SessionConnectedEvent sessionConnectedEvent) { log.debug("connected "); } @EventListener public void handleWebSocketDisconnectListener(SessionDisconnectEvent sessionDisconnectEvent) { StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(sessionDisconnectEvent.getMessage()); log.debug("disconnect, session id = {}", sessionDisconnectEvent.getSessionId()); } @EventListener public void handleWebSocketSubscribeEvent(SessionSubscribeEvent sessionSubscribeEvent) { log.debug("subscribe, {}", sessionSubscribeEvent.toString()); } @EventListener public void handleWebSocketUnsubscribeEvent(SessionUnsubscribeEvent sessionUnsubscribeEvent) { log.debug("unsubscribe, {}", sessionUnsubscribeEvent.getSource()); } }
前端Stomp代码
function chatClient(): CompatClient { const socket = SockJS('http://localhost/ws'); const client = Stomp.over(socket); return client; } client = chatClient(); client.connect( { Authorization: getAuthorization(), }, (frame: any) => { console.log("ws success", frame); client.subscribe(`/user/${userStore.userInfo.username}/messages`, function (message) { console.log('msg', message.body); }); }, (err: any) => { console.log("ws err"); });
问题原因
前后端分离场景下,当页面关闭、刷新或客户端异常断开时,浏览器可能直接终止TCP连接,不会主动发送STOMP标准的DISCONNECT帧给服务器。Spring WebSocket只有在收到明确的DISCONNECT帧或者通过心跳检测到连接超时后,才会触发SessionDisconnectEvent。而非前后端分离环境中,同域页面的关闭流程会触发浏览器的正常断开逻辑,服务器能收到断开信号。
解决方案
1. 前端主动发送断开信号
在页面卸载前(比如beforeunload事件),主动调用Stomp客户端的disconnect方法,确保服务器能收到明确的断开指令:
window.addEventListener('beforeunload', () => { if (client && client.connected) { client.disconnect(() => { console.log('主动断开WebSocket连接'); }); } });
2. 配置服务器端心跳检测
通过配置Spring WebSocket的心跳机制,让服务器主动检测失效连接,超时后触发断开事件:
修改WebSocket配置类
@Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic", "/user"); config.setApplicationDestinationPrefixes("/app"); config.setUserDestinationPrefix("/user"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws") .setAllowedOriginPatterns("*") // 替换为实际前端域名更安全 .withSockJS(); } @Override public void configureWebSocketTransport(WebSocketTransportRegistration registration) { registration.setSendTimeLimit(10000) .setSendBufferSizeLimit(512 * 1024) .setMessageSizeLimit(512 * 1024); } // 配置心跳参数 @Override public void configureClientOutboundChannel(ChannelRegistration registration) { registration.taskExecutor().corePoolSize(4).maxPoolSize(10); } }
添加配置文件参数
在application.properties中添加心跳间隔:
# 服务器发送心跳间隔,单位毫秒 spring.websocket.message-broker.heartbeat.send-interval=30000 # 服务器接收心跳超时时间,单位毫秒 spring.websocket.message-broker.heartbeat.receive-interval=30000
配置后,服务器每30秒向客户端发送心跳包,如果超过30秒未收到客户端响应,就会判定连接失效,触发SessionDisconnectEvent。
3. 检查跨域配置
确保WebSocket端点的跨域配置正确,避免跨域限制导致断开信号无法正常发送:
@Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws") .setAllowedOriginPatterns("https://your-frontend-domain.com") // 指定前端域名 .withSockJS(); }
内容的提问来源于stack exchange,提问作者mouren Zhu
相关产品推荐
相关产品推荐

