Spring Boot + Stomp over WS 与嵌入式 Artemis 代理“目标不存在”
Posted
技术标签:
【中文标题】Spring Boot + Stomp over WS 与嵌入式 Artemis 代理“目标不存在”【英文标题】:Spring Boot + Stomp over WS with embedded Artemis broker "Destination does not exist" 【发布时间】:2015-12-25 09:36:13 【问题描述】:我有一个使用 Spring boot 和 Stomp over Websocket 的示例。当我将代理注册从 SimpleBrokerRegistration 更改为 StompBrokerRelayRegistration 时,它没有按预期工作。
这是我的 Websocket 配置:
@Configuration
@EnableWebSocketMessageBroker
@ConfigurationProperties(prefix = "spring.artemis")
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer
//...
@Override
public void configureMessageBroker(MessageBrokerRegistry config)
// If STOMP broker not configured, create an simple fallback
if (!StringUtil.isEmpty(host) || port > 0)
config.enableStompBrokerRelay("/topic", "/queue")
.setRelayHost(host)
.setRelayPort(port);
else
config.enableSimpleBroker("/topic", "/queue");
config.setApplicationDestinationPrefixes("/app");
@Override
public void registerStompEndpoints(StompEndpointRegistry registry)
registry.addEndpoint("/hello")
.withSockJS();
//...
和 ArtemisConfig:
@Configuration
@ConfigurationProperties(prefix = "spring.artemis")
public class JmsConfig implements ArtemisConfigurationCustomizer
private static final String DEFAULT_TRANSPORT_PROTOCOLS = "STOMP";
private String host;
private int port;
private String protocols;
// ...
@Override
public void customize(org.apache.activemq.artemis.core.config.Configuration configuration)
host = StringUtil.hasText(host)?host:TransportConstants.DEFAULT_HOST;
port = port > 0? port:TransportConstants.DEFAULT_PORT;
protocols = StringUtil.hasText(protocols)?protocols:DEFAULT_TRANSPORT_PROTOCOLS;
Set<TransportConfiguration> acceptors = configuration.getAcceptorConfigurations();
Map<String, Object> params = new HashMap<>();
params.put(TransportConstants.HOST_PROP_NAME, host);
params.put(TransportConstants.PORT_PROP_NAME, port);
params.put(TransportConstants.PROTOCOLS_PROP_NAME, protocols);
TransportConfiguration tc = new TransportConfiguration(NettyAcceptorFactory.class.getName(), params);
acceptors.add(tc);
//...
然后,我使用这样的 javascript 进行连接:
var socket = new SockJS('/hello');
stompClient = Stomp.over(socket);
stompClient.connect('guest', 'guest', function(frame)
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/greetings', function(greeting)
showGreeting(greeting.body);
);
);
它说找不到队列/topic/greetings
当我像这样使用 SimpMessagingTemplate 时:
messagingTemplate.convertAndSend("/topic/greetings", "WARN: " + warningString());
它抛出一个错误:
StompBrokerRelayMessageHandler : Received ERROR message=[AMQ339001: Destination does not exist: /topic/greetings] session=...
我不知道为什么它不能用作 SimpleBroker。
【问题讨论】:
您是否在 Artemis 实例上配置了 stomp-acceptor? 【参考方案1】:您是否事先在 Artemis broker.xml 中创建了此目的地?与 ActiveMQ 不同,您必须这样做,或者在地址设置块中指定自动创建,如下所示:
<!--default for catch all-->
<address-setting match="#">
<dead-letter-address>jms.queue.DLQ</dead-letter-address>
<expiry-address>jms.queue.ExpiryQueue</expiry-address>
<redelivery-delay>0</redelivery-delay>
<max-size-bytes>10485760</max-size-bytes>
<message-counter-history-day-limit>10</message-counter-history-day-limit>
<address-full-policy>BLOCK</address-full-policy>
<auto-create-jms-queues>true</auto-create-jms-queues>
</address-setting>
在这种情况下,将创建任何目的地。但是,这里有一个问题。当你定义一个队列时,像这样:
<queue name="selectorQueue">
<entry name="/queue/selectorQueue"/>
<selector string="color='red'"/>
<durable>true</durable>
</queue>
它实际上将遵循 jms 命名约定,并且实际名称为 jms.queue.selectorQueue
。您也可以尝试映射到该名称。
【讨论】:
在使用 Wildfly 的内置 Artemis 时如何设置 auto-create-jms-queues?这种情况下没有broker.xml...【参考方案2】:出于某种我不知道的原因,Artemis(使用 Stomp 协议)似乎希望主题名称以 jms.topic.
开头,队列名称以 jms.queue.
开头
因此,将您的主题重命名为 jms.topic.greetings
并确保您还将 StompBrokerRelay 配置更改为:
config.enableStompBrokerRelay("jms.topic", "jms.queue")
在此设置中,队列/主题将在需要时自动创建。
(我使用的是 Artemis 1.3。)
【讨论】:
【参考方案3】:对于正在寻找如何在 2021 年将 Spring 的 SimpMessageSendingOperations
(STOMP) 与嵌入式 ActiveMQ Artemis 一起使用的任何人?花了半天时间,我可以告诉你。
添加以下依赖(由于目前存在依赖冲突,请自行查找最新版本)。 reactor-netty
目前是 spring 出于某种原因需要,您可以尝试不使用它。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-artemis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>artemis-jms-server</artifactId>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>artemis-stomp-protocol</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-reactor-netty</artifactId>
</dependency>
创建以下配置类。这是将嵌入式 artemis 服务器暴露给外部虚拟机连接所必需的。据我了解,默认情况下只使用虚拟机内连接,我还没有找到一种方法来使用这些与 stomp 中继。
@Configuration
public class ArenaArtemisConfiguration
private final ArtemisProperties artemisProperties;
public ArenaArtemisConfiguration(ArtemisProperties artemisProperties)
this.artemisProperties = artemisProperties;
@Bean
public ArtemisConfigurationCustomizer customizer()
return configuration ->
try
configuration.addAcceptorConfiguration(
"netty", "tcp://" + artemisProperties.getHost() + ":" + artemisProperties.getPort()
);
catch (Exception e)
throw new IllegalStateException(e);
;
将以下内容添加到您的 application.yml
spring.artemis:
mode: embedded
host: localhost
port: 61616
通过中继配置一个 webSocket 消息代理(在我们的例子中是 artemis)
@Configuration
public class WebSocketConfiguration extends DelegatingWebSocketMessageBrokerConfiguration
private final ArtemisProperties artemisProperties;
public WebSocketConfiguration(ArtemisProperties artemisProperties)
this.artemisProperties = artemisProperties;
@Override
public void configureMessageBroker(MessageBrokerRegistry config)
config.enableStompBrokerRelay("/your-topics")
.setRelayHost(artemisProperties.getHost())
.setRelayPort(artemisProperties.getPort());
其余的应该和enableSimpleBroker
一样。
【讨论】:
以上是关于Spring Boot + Stomp over WS 与嵌入式 Artemis 代理“目标不存在”的主要内容,如果未能解决你的问题,请参考以下文章
Spring stomp over websocket SubscribeMapping 不起作用
Spring STOMP over Websocket - “私人”消息传递
Spring with STOMP over SockJS 和 Tomcat 未升级到 Websockets
STOMP over websockets 与普通 STOMP。哪一个更好?