如何在Spring4中将convertAndSendToUser()与RabbitMQ等外部代理一起使用?

时间:2015-01-18 23:17:53

标签: java spring spring-boot spring-websocket

我正在尝试使用RabbitMQ作为外部代理在Spring 4中配置Web套接字支持,但是当我切换到RabbitMQ时,我在客户端启动时收到以下错误:

'/user/queue/changes' is not a valid destination.
Valid destination types are: /temp-queue, /exchange, /topic, /queue, /amq/queue, /reply-queue/.

在我使用convertAndSendToUser的服务器上,这对于简单的代理可以正常工作,但是一旦切换到RabbitMQ,我就会收到此错误。请注意,RabbitMQ适用于普通主题广播 - 它只是/user频道失效。

我是否需要做一些特别的事情才能让/user与RabbitMQ一起使用?

编辑以包含Web套接字配置

我的WebSocketConfig非常标准,有一些自定义功能可以将其与spring-session集成:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractSessionWebSocketMessageBrokerConfigurer<ExpiringSession> {


@Override
public void configureStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint("/changes").withSockJS();
}

@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
    //config.enableSimpleBroker("/queue", "/topic");
    StompBrokerRelayRegistration r = config.enableStompBrokerRelay("/user", "/topic");

    try {
        String rabbitUrl = System.getenv("CLOUDAMQP_URL");
        if(rabbitUrl != null) { 
            log.info("RABBIT URL detected: " + rabbitUrl);
            URI uri = new URI(rabbitUrl);
            String host = uri.getHost();                
            String login = uri.getUserInfo().split(":",2)[0];
            String passCode = uri.getUserInfo().split(":",2)[1];
            String vhost = uri.getPath().substring(1);
            r.setRelayHost(host);
            r.setSystemLogin(login);    
            r.setSystemPasscode(passCode);
            r.setClientLogin(login);    
            r.setClientPasscode(passCode);
            r.setVirtualHost(vhost);                
        }
    } catch(Exception e) {
        log.error("Error setting up RabbitMQ", e);
    }
    config.setApplicationDestinationPrefixes("/app");
  }
}

4 个答案:

答案 0 :(得分:2)

这是一个很老的问题,但是我遇到了同样的错误,因此我的解决方案可能适用于其他人。这基本上是@Memo 313 MediaSA所说的(以及他的回答被否决的原因)。 我发现的两个关键点是:

  1. 仅设置消息代理支持的前缀(有关rabbitmq,请参见:https://www.rabbitmq.com/stomp.html),并且仅在应用程序中使用它们。
  2. *可选:在configureMessageBroker上时,调用setUserDestinationPrefix并为MessageBrokerRegistry设置用户目的地。(默认值为“ / user /”,因此,如果您只想使用“ / user /”作为用户目的地,则不需要此设置)< / li>

完成这些操作后,一切都应按setUserDestinationPrefix方法的文档说的那样工作:

例如,当用户尝试订阅“ / user / queue / position-updates”时, 目的地可以翻译为“ / queue / position-updatesi9oqdfzo”,从而产生一个 不与尝试执行此操作的任何其他用户冲突的唯一队列名称。 随后,当邮件发送到“ / user / {用户名} / queue / position-updates”时, 目的地将转换为“ / queue / position-updatesi9oqdfzo”。

此外,请确保您的应用程序使用的{username}与principal相同。

答案 1 :(得分:1)

我遇到了类似的错误,但在搜索互联网后我找不到直观的解决方案。

我想在完成RabbitMQ STOMP的文件后分享我的发现。

根据RabbitMQ STOMP的文档,只允许以/exchange/queue/amq/queue/topictemp-queue开头的目的地。目的地/user/*将不被允许。您可以根据邮件要求选择目的地。

  

/ exchange - 发送到任意路由密钥和SUBSCRIBE到任意   绑定模式;

     

/ queue - SEND和SUBSCRIBE到STOMP网关管理的队列;

     

/ amq / queue - 对在STOMP外部创建的队列进行SEND和SUBSCRIBE   网关;

     

/ topic - 发送和订阅瞬态和持久主题;

     

/ temp-queue / - 创建临时队列(仅在回复标题中)。

例如,我想向主题发送消息以通知所有订阅者。

  

对于提供每封邮件副本的简单主题目标   所有活跃订阅者, / topic /&lt;形式的目的地名称&gt; 可以   用过的。主题目标支持AMQP的所有路由模式   主题交流。

     

发送到没有活动订阅者的主题目标的邮件   被丢弃了。

     

AMQP 0-9-1语义

     

对于SEND帧,邮件将发送到 amq.topic 交换   路由密钥&lt;名称&gt;。

     

对于SUBSCRIBE帧,将创建一个自动删除的非持久队列   绑定到 amq.topic 交换路由密钥&lt;名称&gt;。一个   订阅是根据队列创建的。

规范意味着目标为/topic/<name>的stomp子消息将使用rabbitmq的默认交换 amp.topic ,然后可以使用变量名称<创建绑定/ strong>如果它不存在并且还创建了一个队列以通过该绑定绑定交换 amp.topic

如果要创建持久订阅,客户端应发送带有以下标题的订阅消息。

durable:true
auto-delete:false 

在我的应用中,websocket服务器收到消息/watch/{liveid},然后向主题/topic/watchinfo-{liveid}回复另一条消息。

@Secured(User.ROLE_USER)
@MessageMapping("/watch/{liveid}")
@SendTo("/topic/watchinfo-{liveid}")
@JsonView(View.Live.class)
public LiveWatchInfoMessage liveinfo(@DestinationVariable("liveid") String liveid,
                                         @AuthenticationPrincipal UserDetails activeUser) {
        ...
        return LiveWatchInfoMessage.builder().build();
    }

答案 2 :(得分:-1)

我在订阅js方法的开头删除了/,错误不再存在。 旧子:stompClient.subscribe(“ / user / queue / specific-user-user”,....给出错误,新子:stompClient.subscribe(“ user / queue / specific-user-user”没有错误

答案 3 :(得分:-2)

我猜问题就在于此 StompBrokerRelayRegistration r = config.enableStompBrokerRelay(&#34; / user&#34;,&#34; / topic&#34;) 但是你要指定&#34; / queue / loop-changes&#34;在convertAndSendToUser.change StompBrokerRelayRegistration中(&#34; / queue&#34;,&#34; / topic&#34;)。 我有一个后续问题,Spring会将sessionid附加到redditmq的目标。这个sessionid从DefaultUserSessionRegistry获取 - &gt; userSessionIds。它会在集群中工作吗?