11

I can't seem to find a good resource on how to send heartbeats to clients using websockets in Spring!

I have a basic server running using this configuration:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/room");
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/channels").withSockJS();
    }
}

Then I use something like this to send messages to people who subscribed to a room:

this.simpMessagingTemplate.convertAndSend("/room/" + this.roomId, message);

This is the client code used to communicate with the server:

this.connect = function (roomNameParam, connectionCallback) {
    var socket = new SockJS('http://localhost:8080/channels'),

    self.stompClient = Stomp.over(socket);
    self.stompClient.connect({}, function (frame) {
        self.stompClient.subscribe('/room/' + roomNameParam, connectionCallback);
    });
};

I really want to implement heartbeats so the client knows who is connected and to send some data to keep the client and server in sync.

Do I need to manually do it?

Dolan
  • 1,519
  • 4
  • 16
  • 36

3 Answers3

9

Just call:

.setTaskScheduler(heartBeatScheduler());

for the broker config where you want to enable it (works with simple broker too).

@Configuration
public class WebSocketMessageBrokerConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.setApplicationDestinationPrefixes("/app");
        config.enableSimpleBroker("/topic", "/queue", "/user")
                .setTaskScheduler(heartBeatScheduler());
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/endpoint");
    }

    @Bean
    public TaskScheduler heartBeatScheduler() {
        return new ThreadPoolTaskScheduler();
    }

}
Javatar
  • 665
  • 2
  • 10
  • 17
8

The Spring SockJS configuration contains settings for sending heartbeats. By default a heartbeat is sent every 25 seconds assuming no other messages are sent on the connection. See the Spring reference for details.

Bogdan
  • 23,890
  • 3
  • 69
  • 61
  • But what does it actually send? How do I detect this in my Spring Server? There is nothing in my console! – Dolan Mar 08 '15 at 16:43
  • @Dolan: it sends a heartbeat frame (`h`) on the websocket connection (between server and browser). The heartbeat is sent only if no data is sent on a given interval (default 25 sec). It's sent automatically, why do you need to detect it on the server? – Bogdan Mar 08 '15 at 19:17
  • Because I would like to know how many people are connected to the server. So I want heart beats to actually notify me about the details of the people who are connected if that makes sense. Is that possible with Spring/STOMP? – Dolan Mar 11 '15 at 15:13
  • @Dolan: you have as many clients connected to the server as websocket connections you have. You can use something like a global `AtomicInteger` to keep track of connections. When a connection is opened you increment it, when is closed you decrement it. Does something like this fulfill your need? – Bogdan Mar 14 '15 at 16:31
  • That would work provided the internet connection is stable. What if there was a power cut? The global AtomicInteger would never be decremented. I ideally need a heart-beat type implementation. – Dolan Mar 20 '15 at 17:37
  • @Dolan: you have a handler for closed connections. If your client goes away you get notified that the conection has closed and you can decrement the counter. Read the Spring websocket reference/API carefully. – Bogdan Mar 21 '15 at 13:00
  • What about sending other pieces of data? For example, in a game, sending x,y coordinates periodically to the server? Would heartbeats be suitable for that? Thanks – Dolan Mar 23 '15 at 09:10
  • @Dolan: heartbeats are just for protecting you connection for proxies that think it's a hanged TCP connection (TCP is normally not a very long lived connection so a proxy not "seeing" any data exchanged over the websocket for a long time might think it hanged and decide to close it). Otherwise heartbeats are unnecessary. Spring itself cancels the heartbeat task if other data is sent over the wire in the configured heartbeat interval. Data is data, a heartbeat is just a signal to keep proxies at bay. Please read the docs. – Bogdan Mar 23 '15 at 18:16
  • @Bogdan When using STOMP the SockJS heartbeats are disabled. – Gaël Oberson May 15 '15 at 12:37
  • @GaëlOberson: If STOMP heartbeats are enabled, then SockJS heartbeats are disabled – Bogdan May 15 '15 at 20:00
  • @Bogdan Ok, I can see Spring sending a heartbeat every 25 seconds: 22:22:26.017 ... Scheduled heartbeat in session imyo3dws ... 22:22:51.018 ... Preparing to write SockJsFrame content='h' 22:22:51.018 ... Writing SockJsFrame content='h' 22:22:51.018 ... Sending TextMessage payload=[h], ... 22:22:51.018 ... Cancelling heartbeat in session imyo3dws 22:22:51.019 ... Scheduled heartbeat in session imyo3dws **but** no error occurs if the machine creating the session imyo3dws goes offline by pulling the network cable! Do you have any ideas?! – bgraves Jul 07 '15 at 20:34
  • @Bogdan If the browser is closed, even by issuing a 'kill -9', a SessionDisconnectEvent occurs, but this is not the case, if the network cable is pulled. Do you have any ideas?! P.S.: Sorry, I tried to format my last post, but it didn't work out... – bgraves Jul 07 '15 at 20:35
  • @Bogdan Same problem mentioned in https://groups.google.com/forum/#!topic/golang-nuts/L0igp3tax1w – bgraves Jul 07 '15 at 20:39
2

For simple broker you can config heartbeat like this:

<websocket:message-broker application-destination-prefix="/app">
    <websocket:stomp-endpoint path="/wshandler" allowed-origins="*">
    </websocket:stomp-endpoint>
    <websocket:simple-broker prefix="/topic, /queue" heartbeat="10000,10000" scheduler="pingScheduler"/>
</websocket:message-broker>

<bean id="pingScheduler" class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
    <property name="poolSize" value="1"/>
    <property name="threadNamePrefix" value="wss-heartbeat-thread-"/>
</bean>
andrey_tr
  • 69
  • 2
  • 5