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。哪一个更好?

使用 Stomp 在 Spring Boot 中访问 JWT 令牌

如何使用Spring-boot处理越来越多的STOMP主题