Handle STOMP messages from client in order
See gh-21798
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -52,4 +52,18 @@ public interface StompEndpointRegistry {
|
||||
*/
|
||||
WebMvcStompEndpointRegistry setErrorHandler(StompSubProtocolErrorHandler errorHandler);
|
||||
|
||||
/**
|
||||
* Whether to handle client messages sequentially in the order in which
|
||||
* they were received.
|
||||
* <p>By default messages sent to the {@code "clientInboundChannel"} may
|
||||
* be handled in parallel and not in the same order as they were received
|
||||
* because the channel is backed by a ThreadPoolExecutor that in turn does
|
||||
* not guarantee processing in order.
|
||||
* <p>When this flag is set to {@code true} messages within the same session
|
||||
* will be sent to the {@code "clientInboundChannel"} one at a time in
|
||||
* order to preserve the order in which they were received.
|
||||
* @since 6.1
|
||||
*/
|
||||
WebMvcStompEndpointRegistry setPreserveReceiveOrder(boolean preserveReceiveOrder);
|
||||
|
||||
}
|
||||
|
||||
@@ -142,6 +142,15 @@ public class WebMvcStompEndpointRegistry implements StompEndpointRegistry {
|
||||
return this;
|
||||
}
|
||||
|
||||
public WebMvcStompEndpointRegistry setPreserveReceiveOrder(boolean preserveReceiveOrder) {
|
||||
this.stompHandler.setPreserveReceiveOrder(preserveReceiveOrder);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected boolean isPreserveReceiveOrder() {
|
||||
return this.stompHandler.isPreserveReceiveOrder();
|
||||
}
|
||||
|
||||
protected void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.stompHandler.setApplicationEventPublisher(applicationContext);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||
import org.springframework.messaging.simp.SimpSessionScope;
|
||||
import org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler;
|
||||
import org.springframework.messaging.simp.broker.AbstractBrokerMessageHandler;
|
||||
import org.springframework.messaging.simp.broker.OrderedMessageChannelDecorator;
|
||||
import org.springframework.messaging.simp.config.AbstractMessageBrokerConfiguration;
|
||||
import org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler;
|
||||
import org.springframework.messaging.simp.user.SimpUserRegistry;
|
||||
@@ -80,7 +81,8 @@ public abstract class WebSocketMessageBrokerConfigurationSupport extends Abstrac
|
||||
|
||||
@Bean
|
||||
public HandlerMapping stompWebSocketHandlerMapping(
|
||||
WebSocketHandler subProtocolWebSocketHandler, TaskScheduler messageBrokerTaskScheduler) {
|
||||
WebSocketHandler subProtocolWebSocketHandler, TaskScheduler messageBrokerTaskScheduler,
|
||||
AbstractSubscribableChannel clientInboundChannel) {
|
||||
|
||||
WebSocketHandler handler = decorateWebSocketHandler(subProtocolWebSocketHandler);
|
||||
WebMvcStompEndpointRegistry registry =
|
||||
@@ -90,6 +92,7 @@ public abstract class WebSocketMessageBrokerConfigurationSupport extends Abstrac
|
||||
registry.setApplicationContext(applicationContext);
|
||||
}
|
||||
registerStompEndpoints(registry);
|
||||
OrderedMessageChannelDecorator.configureInterceptor(clientInboundChannel, registry.isPreserveReceiveOrder());
|
||||
return registry.getHandlerMapping();
|
||||
}
|
||||
|
||||
|
||||
@@ -108,6 +108,10 @@ public class StompSubProtocolHandler implements SubProtocolHandler, ApplicationE
|
||||
@Nullable
|
||||
private MessageHeaderInitializer headerInitializer;
|
||||
|
||||
private boolean preserveReceiveOrder;
|
||||
|
||||
private final Map<String, MessageChannel> messageChannels = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<String, Principal> stompAuthentications = new ConcurrentHashMap<>();
|
||||
|
||||
@Nullable
|
||||
@@ -193,6 +197,30 @@ public class StompSubProtocolHandler implements SubProtocolHandler, ApplicationE
|
||||
return this.headerInitializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether client messages must be handled in the order received.
|
||||
* <p>By default messages sent to the {@code "clientInboundChannel"} may
|
||||
* not be handled in the same order because the channel is backed by a
|
||||
* ThreadPoolExecutor that in turn does not guarantee processing in order.
|
||||
* <p>When this flag is set to {@code true} messages within the same session
|
||||
* will be sent to the {@code "clientInboundChannel"} one at a time to
|
||||
* preserve the order in which they were received.
|
||||
* @param preserveReceiveOrder whether to publish in order
|
||||
* @since 6.1
|
||||
*/
|
||||
public void setPreserveReceiveOrder(boolean preserveReceiveOrder) {
|
||||
this.preserveReceiveOrder = preserveReceiveOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the handler is configured to handle inbound messages in the
|
||||
* order in which they were received.
|
||||
* @since 6.1
|
||||
*/
|
||||
public boolean isPreserveReceiveOrder() {
|
||||
return this.preserveReceiveOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSupportedProtocols() {
|
||||
return Arrays.asList("v10.stomp", "v11.stomp", "v12.stomp");
|
||||
@@ -268,6 +296,12 @@ public class StompSubProtocolHandler implements SubProtocolHandler, ApplicationE
|
||||
return;
|
||||
}
|
||||
|
||||
MessageChannel channelToUse =
|
||||
(this.messageChannels.computeIfAbsent(session.getId(),
|
||||
id -> this.preserveReceiveOrder ?
|
||||
new OrderedMessageChannelDecorator(outputChannel, logger) :
|
||||
outputChannel));
|
||||
|
||||
for (Message<byte[]> message : messages) {
|
||||
StompHeaderAccessor headerAccessor =
|
||||
MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
|
||||
@@ -307,7 +341,7 @@ public class StompSubProtocolHandler implements SubProtocolHandler, ApplicationE
|
||||
|
||||
try {
|
||||
SimpAttributesContextHolder.setAttributesFromMessage(message);
|
||||
sent = outputChannel.send(message);
|
||||
sent = channelToUse.send(message);
|
||||
|
||||
if (sent) {
|
||||
if (this.eventPublisher != null) {
|
||||
@@ -652,6 +686,7 @@ public class StompSubProtocolHandler implements SubProtocolHandler, ApplicationE
|
||||
outputChannel.send(message);
|
||||
}
|
||||
finally {
|
||||
this.messageChannels.remove(session.getId());
|
||||
this.stompAuthentications.remove(session.getId());
|
||||
SimpAttributesContextHolder.resetAttributes();
|
||||
simpAttributes.sessionCompleted();
|
||||
|
||||
Reference in New Issue
Block a user