-
Notifications
You must be signed in to change notification settings - Fork 38.9k
Description
I discovered that StompBrokerRelayMessageHandler just forwards all messages from webscoket connection. This includes heartbeats from STOMP (over webscoket) clients (including WebSocketStompClient).
This works fine. But there is one caveat here. Not all STOMP messages from webscoket are handled by StompBrokerRelayMessageHandler. Some of them handled by SimpAnnotationMethodMessageHandler because their destination has prefix equal to prefix, that is set in MessageBrokerRegistry.setApplicationDestinationPrefixes.
For example:
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
registry.enableStompBrokerRelay("/topic", "/queue");
}
If WebSocketStompClient just sends a message every 1 second to /app/greeting destination, then this message is handled by controller @MessageMapping("/greeting") and if that controler doesn't forward it to /topic/greeting in the same tcp-session (with RabbitMQ broker) (i.e. returning a value from controller's method), then RabbitMQ never receives that message.
It is common situation, I think. Not all application controllers should send something into broker. Especially into the same tcp connection that associated with websocket, from which message received. For example, I want to send message from my controller, but I want to send it through _system_ connection (not through connection, associated with webscoket, from which the message come) to the broker using SimpMessagingTemplate.
And the problem here is that WebSocketStompClient thinks it sends some bytes (it actually does), so it's org.springframework.messaging.simp.stomp.DefaultStompSession.WriteInactivityTask is never called.
But at the same time these bytes are never delivered to RabbitMQ broker, because they are handled by @MessageMapping controller. So the RabbitMQ doesn't receive any bytes in the heartbeat interval and closes the connection.
[warning] <0.20898.54> STOMP detected missed client heartbeat(s) on connection 192.168.250.203:41574 -> 172.17.0.2:61613, closing it
I think there are two solutions here:
-
Just always send heartbeats from
WebSocketStompClienton specified interval, without relying on whether some bytes were send recently or not.As I see, stompjs does exactly that. It is not exactly how heartbeats should work in STOMP protocol as described here, but it is more defensive (and produces more traffic of course).
-
Do not forward heartbeats from
WebSocketStompClient(and any other connected websocket STOMP client), but do heartbeating with broker internally instead, like it does for_system_connection. At the same time, do not forward heartbeats from broker to the webscoket, and instead do heartbeating with websocket internally. So, there will be two independent sets of heartbeat senders/chekers: one to broker side, and one to websocket side. So, even if the client will send only application messages (and will not send heartbeats because of that), the broker will still receive heartbeats from internal scheduler and will not close the connection.This soulution is more server resource intensive I think. The relay will have to do some scheduling instead of just relaying heartbeats back and forward, but it looks like a more "right" solution, that doesn't violate STOMP protocol (in the sense of not sending heartbeats to the broker in specified interval because of
@MessageMapping).While I think this solution is more "right", I'm worried about performance here. For example, to service 10k webscoket connections, it should be 10k
onWriteInactivityand 10konReadInactivitychecks on both sides , total 40k.