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

Spring Boot接收StompJS心跳(无SockJS)及服务器发心跳方法

关于Spring Boot与StompJS WebSocket心跳的问题

一、Angular端已配置的心跳设置

我在Angular中通过StompJS设置了客户端心跳,代码为this.client.heartbeat.outgoing = 2000;,完整Angular代码如下:

this.client = new Client({
  brokerURL: 'ws://localhost:8080/gs-guide-websocket',
  onConnect: () => {
    this.client.subscribe('/topic/greetings/' + this.username, (messages: { body }) => {
      // 处理消息
    });
    this.client.heartbeat.outgoing = 2000;
  }
});
this.client.activate();

二、当前Spring Boot配置

我的Spring Boot WebSocket配置代码如下:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/app");
    }
    
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/gs-guide-websocket").setAllowedOrigins("http://localhost:4200");
        System.out.println("entered the websocket connection");
    }
}

三、疑问与需求

  1. 如何在Spring Boot中捕获客户端发送的心跳,以此定期了解哪些客户端处于在线连接状态?
  2. Spring Boot服务器能否主动向客户端发送心跳?如果可以,如何实现?

解决方案

1. 捕获客户端心跳与跟踪在线客户端

实际上,不需要直接捕获心跳包来跟踪在线状态,Spring WebSocket提供了更直接的会话事件监听方式:

方式一:监听连接/断开事件维护在线列表

通过监听Spring发布的SessionConnectEvent和SessionDisconnectEvent,可以在客户端连接、断开时更新在线客户端列表:

@Component
public class WebSocketSessionListener {
    // 用线程安全的Map存储在线客户端(SessionID -> 用户名)
    private final Map<String, String> onlineClients = new ConcurrentHashMap<>();

    @EventListener
    public void onClientConnect(SessionConnectEvent event) {
        StompHeaderAccessor accessor = StompHeaderAccessor.wrap(event.getMessage());
        // 从连接头中获取客户端传递的用户名(Angular端需提前配置)
        String username = accessor.getNativeHeader("username").get(0);
        String sessionId = accessor.getSessionId();
        onlineClients.put(sessionId, username);
        System.out.println("客户端上线:" + username + ",SessionID:" + sessionId);
    }

    @EventListener
    public void onClientDisconnect(SessionDisconnectEvent event) {
        StompHeaderAccessor accessor = StompHeaderAccessor.wrap(event.getMessage());
        String sessionId = accessor.getSessionId();
        String username = onlineClients.remove(sessionId);
        System.out.println("客户端下线:" + username + ",SessionID:" + sessionId);
    }
}

对应Angular端需要在连接时传递用户名:

this.client = new Client({
  brokerURL: 'ws://localhost:8080/gs-guide-websocket',
  connectHeaders: {
    username: this.username // 向服务端传递用户名
  },
  onConnect: () => {
    this.client.heartbeat.outgoing = 2000;
    this.client.subscribe('/topic/greetings/' + this.username, (messages: { body }) => {
      // 处理消息
    });
  }
});
方式二:直接拦截心跳帧

如果需要主动捕获心跳包,可以自定义ChannelInterceptor拦截所有入站消息,判断是否为心跳帧:

@Component
public class HeartbeatInterceptor implements ChannelInterceptor {
    @Override
    public Message<?> preSend(Message<?> message, MessageChannel channel) {
        StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
        if (accessor != null && StompCommand.SEND.equals(accessor.getCommand())) {
            // 心跳帧通常是空内容,且无目标地址
            byte[] payload = (byte[]) message.getPayload();
            if (payload.length == 0 && accessor.getDestination() == null) {
                String sessionId = accessor.getSessionId();
                System.out.println("收到客户端心跳:" + sessionId);
                // 可在此更新客户端最后活跃时间
            }
        }
        return message;
    }
}

然后在WebSocket配置中注册拦截器:

@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
    registration.interceptors(new HeartbeatInterceptor());
}

2. 服务器主动向客户端发送心跳

Spring WebSocket默认支持服务器端心跳,只需在配置中设置心跳间隔即可:

@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
    config.enableSimpleBroker("/topic")
          // 数组第一个值是服务器发送心跳的间隔(毫秒),第二个是期望收到客户端心跳的间隔
          .setHeartbeatValue(new long[]{10000, 10000}); 
    config.setApplicationDestinationPrefixes("/app");
}

同时Angular端需配置对应的心跳接收间隔,确保两端匹配:

this.client = new Client({
  brokerURL: 'ws://localhost:8080/gs-guide-websocket',
  heartbeat: {
    outgoing: 2000,  // 客户端每2秒发心跳
    incoming: 10000  // 客户端期望每10秒收到服务器心跳
  },
  connectHeaders: {
    username: this.username
  },
  onConnect: () => {
    this.client.subscribe('/topic/greetings/' + this.username, (messages: { body }) => {
      // 处理消息
    });
  }
});

内容的提问来源于stack exchange,提问作者UltraLegend

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 11:35:04