You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot STOMP WebSocket消息已发至队列但订阅客户端未接收

Spring Boot + STOMP WebSocket 实时通知异常排查

问题背景

使用Spring Boot结合STOMP实现WebSocket实时通知功能,已配置WebSocket配置、安全配置及WebSocket处理器装饰器。

问题现象

  • 可通过WebSocketKing成功连接到ws://localhost:8080/notification/websocket端点,握手升级正常;
  • 不同客户端能成功订阅/queue/unseen-bookings-1和/queue/unseen-bookings-2,日志确认订阅已处理;
  • 调用PATCH端点更新预订时,通过messagingTemplate.convertAndSend()向对应队列发送通知,日志显示消息已广播,但两个客户端均未收到消息。

相关日志

订阅日志

2025-03-24 01:46:57 2025-03-24T01:46:57.940Z TRACE 1 --- [   XNIO-1 I/O-2] s.w.s.h.LoggingWebSocketHandlerDecorator : Handling TextMessage payload=[SUBSCRIBE_..], byteCount=54, last=true] in StandardWebSocketSession[id=0fe8173f-9c1b-6ce1-658e-38a33e556d1c, uri=/notification/websocket]  
2025-03-24 01:46:57 2025-03-24T01:46:57.945Z TRACE 1 --- [   XNIO-1 I/O-2] o.s.messaging.simp.stomp.StompDecoder    : Decoded SUBSCRIBE {id=["1"], destination=[/queue/unseen-bookings-1]} session=null 
2025-03-24 01:46:57 2025-03-24T01:46:57.946Z TRACE 1 --- [   XNIO-1 I/O-2] o.s.w.s.m.StompSubProtocolHandler        : From client: SUBSCRIBE /queue/unseen-bookings-1 id="1" session=0fe8173f-9c1b-6ce1-658e-38a33e556d1c 
2025-03-24 01:46:57 2025-03-24T01:46:57.949Z DEBUG 1 --- [nboundChannel-2] o.s.m.s.b.SimpleBrokerMessageHandler     : Processing SUBSCRIBE /queue/unseen-bookings-1 id="1" session=0fe8173f-9c1b-6ce1-658e-38a33e556d1c 
2025-03-24 01:47:03 2025-03-24T01:47:03.493Z  INFO 1 --- [MessageBroker-1] o.s.w.s.c.WebSocketMessageBrokerStats    : WebSocketSession[1 current WS(1)-HttpStream(0)-HttpPoll(0), 1 total, 0 closed abnormally (0 connect failure, 0 send limit, 0 transport error)], stompSubProtocol[processed CONNECT(0)-CONNECTED(0)-DISCONNECT(0)], stompBrokerRelay[null], inboundChannel[pool size = 3, active threads = 0, queued tasks = 0, completed tasks = 3], outboundChannel[pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0], sockJsScheduler[pool size = 3, active threads = 1, queued tasks = 2, completed tasks = 0]  

消息广播日志

Processing MESSAGE destination=/queue/unseen-bookings-1 session=null payload={"ownerUnseen":0,"renterUnseen":0}  
Broadcasting to 1 sessions.  
Processing MESSAGE destination=/queue/unseen-bookings-2 session=null payload={"ownerUnseen":0,"renterUnseen":1}  
Broadcasting to 1 sessions.  

相关配置

已配置WebsocketSecurityConfiguration.java、WebsocketConfiguration.java、EmaWebSocketHandlerDecorator.java及PATCH端点代码。

疑问

  1. 为何日志显示消息已广播,但订阅客户端未接收?
  2. 日志中的session=null是否相关?
  3. 使用SimpleBroker时session是否重要?
  4. 是否存在阻止消息到达客户端的配置?

问题分析与解决方案

1. session=null的核心影响

日志里的session=null是关键问题:

  • STOMP协议要求客户端必须先发送CONNECT帧,待服务器返回CONNECTED帧后,才能建立有效会话上下文。但统计日志显示stompSubProtocol[processed CONNECT(0)-CONNECTED(0)-DISCONNECT(0)],说明客户端从未发送CONNECT帧,或服务器未正确处理该请求。
  • 无有效会话的订阅属于“无状态”订阅,SimpleBroker无法将消息关联到具体的WebSocket会话,即便日志显示“Broadcasting to 1 sessions”,实际也无法完成投递。

2. 排查与修复方向

客户端侧

  • 确认客户端在建立WebSocket连接后,是否正确发送STOMP的CONNECT帧(若开启安全校验,需携带认证信息)。
  • 确保客户端在收到CONNECTED帧后,再发送SUBSCRIBE帧。部分WebSocket测试工具(如WebSocketKing)仅建立底层连接,不会自动发送STOMP的CONNECT请求,导致会话未初始化。

服务端配置

  • WebSocket核心配置:检查@EnableWebSocketMessageBroker的配置,确保simpleBroker()包含/queue前缀:
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/queue", "/topic");
        config.setApplicationDestinationPrefixes("/app");
    }
    
  • 安全配置:若WebSocketSecurityConfiguration拦截了STOMP端点,需放行CONNECT和SUBSCRIBE类型的请求(根据业务调整认证规则):
    @Override
    protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
        messages.simpDestMatchers("/queue/**").authenticated()
                .simpTypeMatchers(CONNECT, SUBSCRIBE).permitAll();
    }
    
  • 处理器装饰器:检查EmaWebSocketHandlerDecorator是否拦截或修改了出站消息,确保handleMessage方法正确传递消息,未做无意义的拦截。

消息发送逻辑

  • 确认messagingTemplate.convertAndSend()的目标路径与客户端订阅路径完全一致,避免前缀错误(比如多了/app前缀),正确写法示例:
    messagingTemplate.convertAndSend("/queue/unseen-bookings-1", notificationPayload);
    

3. 验证步骤

  1. 使用支持STOMP的客户端(如Postman、STOMP.js)重新测试:先发送CONNECT帧,确认收到CONNECTED后再订阅队列,最后触发PATCH接口。
  2. 开启org.springframework.messaging的TRACE级日志,追踪消息从发送到出站的全链路,确认是否在某个环节被拦截。

内容的提问来源于stack exchange,提问作者Charfeddine Mohamed Ali

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 18:15:21