Remove CONNECT-related message buffer from STOMP relay
Before this change, the StompProtocolHandler always responded to clients with a CONNECTED frame, while the STOMP broker relay independantly forwarded the client CONNECT to the broker and waited for the CONNECTED frame back. That meant the relay had to buffer client messages until it received the CONNECTED response from the message broker. This change ensures that clients wait for a CONNECTED frame from the message broker. The broker relay forwards the CONNECT frame to the broker. The broker responds with a CONNECTED frame, which the relay then forwards to the client. As a result, a (well-written) client will not send any messages to the relay until the connection to the broker is fully established. The StompProtcolHandler can now be configured whether to send CONNECTED frame back. By default that is off. So when using the simple broker, the StompProtocolHandler can still respond with CONNECTED frames. The relay's handling of a connection being dropped has also been improved. When a connection for a client relay session is dropped an ERROR frame will be sent back to the client. If a connection is closed as part of a DISCONNECT frame being sent, no ERROR frame is sent back to the client. When the connection for the system relay session is dropped, an event is published indicating that the broker is unavailable. Reactor's TcpClient will then attempt to re-restablish the connection.
This commit is contained in:
committed by
Rossen Stoyanchev
parent
a489c2cf38
commit
8d2a376b0f
@@ -54,7 +54,8 @@ public class ServletStompEndpointRegistry implements StompEndpointRegistry {
|
||||
|
||||
|
||||
public ServletStompEndpointRegistry(WebSocketHandler webSocketHandler,
|
||||
MutableUserQueueSuffixResolver userQueueSuffixResolver, TaskScheduler defaultSockJsTaskScheduler) {
|
||||
MutableUserQueueSuffixResolver userQueueSuffixResolver, TaskScheduler defaultSockJsTaskScheduler,
|
||||
boolean handleConnect) {
|
||||
|
||||
Assert.notNull(webSocketHandler);
|
||||
Assert.notNull(userQueueSuffixResolver);
|
||||
@@ -63,6 +64,7 @@ public class ServletStompEndpointRegistry implements StompEndpointRegistry {
|
||||
this.subProtocolWebSocketHandler = findSubProtocolWebSocketHandler(webSocketHandler);
|
||||
this.stompHandler = new StompProtocolHandler();
|
||||
this.stompHandler.setUserQueueSuffixResolver(userQueueSuffixResolver);
|
||||
this.stompHandler.setHandleConnect(handleConnect);
|
||||
this.sockJsScheduler = defaultSockJsTaskScheduler;
|
||||
}
|
||||
|
||||
|
||||
@@ -57,8 +57,10 @@ public abstract class WebSocketMessageBrokerConfigurationSupport {
|
||||
|
||||
@Bean
|
||||
public HandlerMapping brokerWebSocketHandlerMapping() {
|
||||
ServletStompEndpointRegistry registry = new ServletStompEndpointRegistry(
|
||||
subProtocolWebSocketHandler(), userQueueSuffixResolver(), brokerDefaultSockJsTaskScheduler());
|
||||
boolean brokerRelayConfigured = getMessageBrokerConfigurer().getStompBrokerRelay() != null;
|
||||
ServletStompEndpointRegistry registry = new ServletStompEndpointRegistry(subProtocolWebSocketHandler(),
|
||||
userQueueSuffixResolver(), brokerDefaultSockJsTaskScheduler(), !brokerRelayConfigured);
|
||||
|
||||
registerStompEndpoints(registry);
|
||||
AbstractHandlerMapping hm = registry.getHandlerMapping();
|
||||
hm.setOrder(1);
|
||||
|
||||
@@ -17,13 +17,9 @@
|
||||
package org.springframework.messaging.simp.stomp;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
@@ -249,12 +245,8 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
|
||||
|
||||
private final String sessionId;
|
||||
|
||||
private final BlockingQueue<Message<?>> messageQueue = new LinkedBlockingQueue<Message<?>>(50);
|
||||
|
||||
private volatile StompConnection stompConnection = new StompConnection();
|
||||
|
||||
private final Object monitor = new Object();
|
||||
|
||||
|
||||
private RelaySession(String sessionId) {
|
||||
Assert.notNull(sessionId, "sessionId is required");
|
||||
@@ -291,6 +283,12 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
|
||||
|
||||
protected void handleTcpConnection(TcpConnection<Message<byte[]>, Message<byte[]>> tcpConn, final Message<?> connectMessage) {
|
||||
this.stompConnection.setTcpConnection(tcpConn);
|
||||
tcpConn.on().close(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
connectionClosed();
|
||||
}
|
||||
});
|
||||
tcpConn.in().consume(new Consumer<Message<byte[]>>() {
|
||||
@Override
|
||||
public void accept(Message<byte[]> message) {
|
||||
@@ -307,12 +305,8 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
|
||||
|
||||
StompHeaderAccessor headers = StompHeaderAccessor.wrap(message);
|
||||
if (StompCommand.CONNECTED == headers.getCommand()) {
|
||||
synchronized(this.monitor) {
|
||||
this.stompConnection.setReady();
|
||||
publishBrokerAvailableEvent();
|
||||
flushMessages();
|
||||
}
|
||||
return;
|
||||
this.stompConnection.setReady();
|
||||
publishBrokerAvailableEvent();
|
||||
}
|
||||
|
||||
headers.setSessionId(this.sessionId);
|
||||
@@ -344,24 +338,11 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
|
||||
public void forward(Message<?> message) {
|
||||
|
||||
if (!this.stompConnection.isReady()) {
|
||||
synchronized(this.monitor) {
|
||||
if (!this.stompConnection.isReady()) {
|
||||
this.messageQueue.add(message);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Not connected, message queued. Queue size=" + this.messageQueue.size());
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
logger.warn("Message sent to relay before it was CONNECTED. Discarding message: " + message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.messageQueue.isEmpty()) {
|
||||
forwardInternal(message);
|
||||
}
|
||||
else {
|
||||
this.messageQueue.add(message);
|
||||
flushMessages();
|
||||
}
|
||||
forwardInternal(message);
|
||||
}
|
||||
|
||||
private boolean forwardInternal(final Message<?> message) {
|
||||
@@ -381,6 +362,12 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
|
||||
logger.trace("Forwarding to STOMP broker, message: " + message);
|
||||
}
|
||||
|
||||
StompCommand command = StompHeaderAccessor.wrap(message).getCommand();
|
||||
|
||||
if (command == StompCommand.DISCONNECT) {
|
||||
this.stompConnection.setDisconnected();
|
||||
}
|
||||
|
||||
final Deferred<Boolean, Promise<Boolean>> deferred = new DeferredPromiseSpec<Boolean>().get();
|
||||
tcpConnection.send((Message<byte[]>)message, new Consumer<Boolean>() {
|
||||
@Override
|
||||
@@ -396,7 +383,7 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
|
||||
handleTcpClientFailure("Timed out waiting for message to be forwarded to the broker", null);
|
||||
}
|
||||
else if (!success) {
|
||||
if (StompHeaderAccessor.wrap(message).getCommand() != StompCommand.DISCONNECT) {
|
||||
if (command != StompCommand.DISCONNECT) {
|
||||
handleTcpClientFailure("Failed to forward message to the broker", null);
|
||||
}
|
||||
}
|
||||
@@ -408,13 +395,10 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
|
||||
return (success != null) ? success : false;
|
||||
}
|
||||
|
||||
private void flushMessages() {
|
||||
List<Message<?>> messages = new ArrayList<Message<?>>();
|
||||
this.messageQueue.drainTo(messages);
|
||||
for (Message<?> message : messages) {
|
||||
if (!forwardInternal(message)) {
|
||||
return;
|
||||
}
|
||||
protected void connectionClosed() {
|
||||
relaySessions.remove(this.sessionId);
|
||||
if (this.stompConnection.isReady()) {
|
||||
sendError("Lost connection to the broker");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -461,6 +445,10 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
|
||||
|
||||
private class SystemRelaySession extends RelaySession {
|
||||
|
||||
private static final long HEARTBEAT_SEND_INTERVAL = 10000;
|
||||
|
||||
private static final long HEARTBEAT_RECEIVE_INTERVAL = 10000;
|
||||
|
||||
public static final String ID = "stompRelaySystemSessionId";
|
||||
|
||||
|
||||
@@ -473,7 +461,7 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
|
||||
headers.setAcceptVersion("1.1,1.2");
|
||||
headers.setLogin(systemLogin);
|
||||
headers.setPasscode(systemPasscode);
|
||||
headers.setHeartbeat(0,0);
|
||||
headers.setHeartbeat(HEARTBEAT_SEND_INTERVAL, HEARTBEAT_RECEIVE_INTERVAL);
|
||||
Message<?> connectMessage = MessageBuilder.withPayloadAndHeaders(new byte[0], headers).build();
|
||||
super.connect(connectMessage);
|
||||
}
|
||||
@@ -488,6 +476,11 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void connectionClosed() {
|
||||
publishBrokerUnavailableEvent();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendMessageToClient(Message<?> message) {
|
||||
StompHeaderAccessor headers = StompHeaderAccessor.wrap(message);
|
||||
|
||||
@@ -70,6 +70,7 @@ public class StompProtocolHandler implements SubProtocolHandler {
|
||||
|
||||
private MutableUserQueueSuffixResolver queueSuffixResolver = new SimpleUserQueueSuffixResolver();
|
||||
|
||||
private volatile boolean handleConnect = false;
|
||||
|
||||
/**
|
||||
* Configure a resolver to use to maintain queue suffixes for user
|
||||
@@ -86,6 +87,29 @@ public class StompProtocolHandler implements SubProtocolHandler {
|
||||
return this.queueSuffixResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the handling of CONNECT frames. When {@code true}, CONNECT
|
||||
* frames will be handled by this handler, and a CONNECTED response will be
|
||||
* sent. When {@code false}, CONNECT frames will be forwarded for
|
||||
* handling by another component.
|
||||
*
|
||||
* @param handleConnect {@code true} if connect frames should be handled
|
||||
* by this handler, {@code false} otherwise.
|
||||
*/
|
||||
public void setHandleConnect(boolean handleConnect) {
|
||||
this.handleConnect = handleConnect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this handler will handle CONNECT frames.
|
||||
*
|
||||
* @return Returns {@code true} if this handler will handle CONNECT frames,
|
||||
* otherwise {@code false}.
|
||||
*/
|
||||
public boolean willHandleConnect() {
|
||||
return this.handleConnect;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSupportedProtocols() {
|
||||
return Arrays.asList("v10.stomp", "v11.stomp", "v12.stomp");
|
||||
@@ -121,17 +145,17 @@ public class StompProtocolHandler implements SubProtocolHandler {
|
||||
|
||||
message = MessageBuilder.withPayloadAndHeaders(message.getPayload(), headers).build();
|
||||
|
||||
if (SimpMessageType.CONNECT.equals(headers.getMessageType())) {
|
||||
if (this.handleConnect && SimpMessageType.CONNECT.equals(headers.getMessageType())) {
|
||||
handleConnect(session, message);
|
||||
}
|
||||
|
||||
outputChannel.send(message);
|
||||
else {
|
||||
outputChannel.send(message);
|
||||
}
|
||||
}
|
||||
catch (Throwable t) {
|
||||
logger.error("Terminating STOMP session due to failure to send message: ", t);
|
||||
sendErrorMessage(session, t);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,8 +168,8 @@ public class StompProtocolHandler implements SubProtocolHandler {
|
||||
StompHeaderAccessor headers = StompHeaderAccessor.wrap(message);
|
||||
headers.setCommandIfNotSet(StompCommand.MESSAGE);
|
||||
|
||||
if (StompCommand.CONNECTED.equals(headers.getCommand())) {
|
||||
// Ignore for now since we already sent it
|
||||
if (this.handleConnect && StompCommand.CONNECTED.equals(headers.getCommand())) {
|
||||
// Ignore since we already sent it
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user