STOMP连接因心跳丢失关闭:前端有Ping-Pong仍报错求助
错误日志
Received ERROR {message=[AMQ229014: Did not receive data from /192.0.2.1:46748 within the 20000ms connection TTL. The connection will now be closed.]} session=rgmekuxj
环境配置
- 前端:使用SockJS和Stomp.js,浏览器控制台可见Ping-Pong交互,确认客户端已发送心跳。
- 后端:Spring Boot,配置
StompBrokerRelayMessageHandler设置心跳间隔:
public void configureMessageBroker(MessageBrokerRegistry config) { int sendInterval = 10000; // 发送间隔(毫秒) int receiveInterval = (int) (sendInterval * heartBeatReceiveScale); config.setApplicationDestinationPrefixes("/app"); config.enableStompBrokerRelay("/topic", "/queue") .setUserDestinationBroadcast("/topic/random") .setUserRegistryBroadcast("/topic/simp-user-registry") .setTcpClient(createTcpClient()) .setSystemLogin(username) .setSystemPasscode(password) .setClientLogin(username) .setClientPasscode(password) .setSystemHeartbeatSendInterval(sendInterval) // 设置系统心跳发送间隔 .setSystemHeartbeatReceiveInterval(receiveInterval); }
后端日志片段:
2024-11-29T DEBUG 40756 --- [NotificationService] [ent-scheduler-5] o.s.m.s.s.StompBrokerRelayMessageHandler : Received CONNECTED heart-beat=[10000, 10000] session=itwv0lto 2024-11-29T DEBUG 13048 --- [NotificationService] [MessageBroker-6] o.s.m.s.s.StompBrokerRelayMessageHandler : Forwarding SEND /topic/simp-user-registry session=_system_ application/json payload={...}
此外,使用自定义ReactorNettyTcpClient处理Broker连接及故障转移重试逻辑。
观察结果
- 前端控制台显示Ping-Pong交互,心跳已发送。
- 后端日志显示CONNECTED帧指定
heart-beat=[10000, 10000]。
但连接仍在20秒TTL超时后被关闭。
问题
- 如何确认后端是否正确接收到心跳?
- ActiveMQ Artemis或Spring Boot中是否有特定配置需调整以解决该问题?
解决方案
1. 确认后端是否接收心跳的方法
开启TRACE级日志:
在Spring Boot配置文件中,将STOMP和Artemis相关日志级别设为TRACE,可查看心跳帧的收发细节:logging: level: org.springframework.messaging.simp.stomp: TRACE org.apache.activemq.artemis.core.protocol.stomp: TRACE若日志中出现
Received STOMP PING或Sending STOMP PING条目,说明心跳正常收发;若只有发送记录无接收记录,说明心跳在传输中丢失或未被正确转发。抓包验证:
使用Wireshark或tcpdump抓取前端到Spring Boot、Spring Boot到Artemis的TCP数据包,检查是否有STOMP心跳帧(空帧,仅含\n\n)传输。
2. 配置调整建议
Spring Boot端调整
配置客户端与Spring Boot的心跳:
当前仅设置了Spring Boot与Broker的systemHeartbeat,需额外配置客户端与Spring Boot之间的心跳:@Override public void configureWebSocketTransport(WebSocketTransportRegistration registration) { registration.setHeartbeatTime(10000, 10000); // 客户端与Spring Boot的心跳间隔 registration.setSendTimeLimit(10000) .setSendBufferSizeLimit(512 * 1024) .setTimeToFirstMessage(30000); }确保各层级心跳间隔匹配,避免中间层未转发心跳。
检查自定义TcpClient的心跳处理:
自定义ReactorNettyTcpClient需确保不过滤STOMP空心跳帧,可添加如下处理:private ReactorNettyTcpClient<byte[]> createTcpClient() { return new ReactorNettyTcpClient<>( client -> client.host("broker-host").port(61613) .handle((inbound, outbound) -> { inbound.receive().subscribe(); return outbound.send(inbound.receive()).then(); }), new StompReactorNettyCodec() ); }
ActiveMQ Artemis端调整
调整连接TTL与心跳的匹配关系:
Artemis默认connection-ttl为20000ms,需将其设为心跳间隔的2-3倍,避免网络延迟导致误判。修改broker.xml的STOMP acceptor:<acceptor name="stomp-acceptor">tcp://0.0.0.0:61613?protocols=STOMP;connectionTtl=30000</acceptor>启用STOMP心跳支持:
在STOMP acceptor配置中添加stompHeartBeatSupport=true,强制Artemis处理STOMP心跳:<acceptor name="stomp-acceptor">tcp://0.0.0.0:61613?protocols=STOMP;connectionTtl=30000;stompHeartBeatSupport=true</acceptor>
内容的提问来源于stack exchange,提问作者abhishek yadav

