简体   繁体   English

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

[英]How do I use convertAndSendToUser() with an external broker such as RabbitMQ in Spring4?

I am trying to configure web socket support in Spring 4 using RabbitMQ as the external broker, but as soon as I switch to RabbitMQ, I get the following error on start-up in the client:我正在尝试使用 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/.

On the server I am using convertAndSendToUser and this works fine with the simple broker, but as soon as I switch to RabbitMQ, I get this error.在服务器上,我正在使用convertAndSendToUser ,这在简单的代理上运行良好,但是一旦我切换到 RabbitMQ,我就会收到此错误。 Note that RabbitMQ works fine for normal topic broadcasts - it's just the /user channel that falls over.请注意,RabbitMQ 适用于普通主题广播 - 它只是/user频道失败。

Do I need to do something special to get /user to work with RabbitMQ?我是否需要做一些特别的事情才能让/user与 RabbitMQ 一起工作?

Edit to include Web Socket Config编辑以包含 Web Socket 配置

My WebSocketConfig is pretty standard with a few customisations to integrate it with spring-session :我的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");
  }
}

I met the similar error, but I didn't find an intuitive solution after searching Internet.遇到了类似的错误,但是在网上搜索后没有找到直观的解决方法。

I would like to share my findings after going throught the doc of RabbitMQ STOMP .我想在阅读RabbitMQ STOMP的文档后分享我的发现。

According to the doc of RabbitMQ STOMP , only destination starting with /exchange , /queue , /amq/queue , /topic and temp-queue are allowed.根据RabbitMQ STOMP的文档,只允许以/exchange/queue/amq/queue/topictemp-queue开头的目的地。 The destination /user/* would NOT be allowed.目标/user/*将不被允许。 You can choose the destination per the requirements of your message.您可以根据消息的要求选择目的地。

/exchange -- SEND to arbitrary routing keys and SUBSCRIBE to arbitrary binding patterns; /exchange -- 发送到任意路由键并订阅任意绑定模式;

/queue -- SEND and SUBSCRIBE to queues managed by the STOMP gateway; /queue -- 发送和订阅由 STOMP 网关管理的队列;

/amq/queue -- SEND and SUBSCRIBE to queues created outside the STOMP gateway; /amq/queue -- 发送和订阅在 STOMP 网关外创建的队列;

/topic -- SEND and SUBSCRIBE to transient and durable topics; /topic -- 发送和订阅临时和持久的主题;

/temp-queue/ -- create temporary queues (in reply-to headers only). /temp-queue/ -- 创建临时队列(仅在回复标头中)。

For example, I would like to send a message to a topic to notify all subscribers.例如,我想向某个主题发送消息以通知所有订阅者。

For simple topic destinations which deliver a copy of each message to all active subscribers, destinations of the form /topic/< name > can be used.对于将每条消息的副本传递给所有活动订阅者的简单主题目的地,可以使用/topic/<name>形式的目的地。 Topic destinations support all the routing patterns of AMQP topic exchanges.主题目的地支持 AMQP 主题交换的所有路由模式。

Messages sent to a topic destination that has no active subscribers are simply discarded.发送到没有活动订阅者的主题目的地的消息将被简单地丢弃。

AMQP 0-9-1 Semantics AMQP 0-9-1 语义

For SEND frames, the message is sent to the amq.topic exchange with the routing key < name >.对于 SEND 帧,消息被发送到带有路由键 <name> 的amq.topic交换。

For SUBSCRIBE frames, an autodeleted, non-durable queue is created and bound to the amq.topic exchange with routing key < name >.对于 SUBSCRIBE 帧,会创建一个自动删除的非持久队列,并使用路由键 <name> 绑定到amq.topic交换。 A subscription is created against the queue.针对队列创建订阅。

The spec means that the stomp sub message with destination /topic/<name> will use the default exchange amp.topic of rabbitmq, then a binding might be created with the variable name if it does not exist and a queue also be created to bind the exchange amp.topic by that binding.该规范意味着目标为/topic/<name>的 stomp 子消息将使用rabbitmq的默认交换amp.topic ,如果变量名称不存在,则可能会创建一个绑定,并创建一个队列来绑定通过该绑定交换amp.topic

If you want to create a durable subscription, the client should send subscribe message with below headers.如果要创建持久订阅,客户端应发送带有以下标头的订阅消息。

durable:true
auto-delete:false 

In my app the websocket server receives message /watch/{liveid} , then reply another message to topic /topic/watchinfo-{liveid} .在我的应用程序中,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();
    }

It is quite an old question, but I just met the same error so my solution might works for others.这是一个很老的问题,但我刚刚遇到了同样的错误,所以我的解决方案可能适用于其他人。 It's basicly what @Memo 313 MediaSA said(and why his answer is downvoted).这基本上是@Memo 313 MediaSA 所说的(以及为什么他的回答被否决)。 The two key points I have found are:我发现的两个关键点是:

  1. set only the prefix that message broker support(for rabbitmq, see: https://www.rabbitmq.com/stomp.html ), and only use them in application.仅设置消息代理支持的前缀(对于rabbitmq,请参阅: https ://www.rabbitmq.com/stomp.html),并且仅在应用程序中使用它们。
  2. *optional: call setUserDestinationPrefix and set a user destinations for MessageBrokerRegistry when over configureMessageBroker.(the default is "/user/" so if you just want to use "/user/" as user destinations this setting is not necessary) *可选:调用 setUserDestinationPrefix 并在超过 configureMessageBroker 时为 MessageBrokerRegistry 设置用户目的地。(默认为“/user/”,因此如果您只想使用“/user/”作为用户目的地,则不需要此设置)

After doing those, everything should work as what doc for setUserDestinationPrefix methord says:完成这些之后,一切都应该像 setUserDestinationPrefix 方法的文档所说的那样:

For example when a user attempts to subscribe to "/user/queue/position-updates", the destination may be translated to "/queue/position-updatesi9oqdfzo" yielding a unique queue name that does not collide with any other user attempting to do the same.例如,当用户尝试订阅“/user/queue/position-updates”时,目的地可能会被转换为“/queue/position-updatesi9oqdfzo”,从而产生一个唯一的队列名称,该名称不会与任何其他尝试执行的用户发生冲突相同。 Subsequently when messages are sent to "/user/{username}/queue/position-updates", the destination is translated to "/queue/position-updatesi9oqdfzo".随后,当消息发送到“/user/{username}/queue/position-updates”时,目的地被转换为“/queue/position-updatesi9oqdfzo”。

Also, be sure that the {username} your application is using is the same with principal.此外,请确保您的应用程序使用的 {username} 与主体相同。

Remove the "/user" from config.enableStompBrokerRelay("/user", "/topic");config.enableStompBrokerRelay("/user", "/topic");删除"/user" config.enableStompBrokerRelay("/user", "/topic"); and add the "/queue" , spring automatically adds the /user when you send-to-a-specific user并添加"/queue"当您send-to-a-specific userspring 会自动添加/user send-to-a-specific user

And just sendTo(username, "/queue/(what_you_want)", msg) and subscibe to "/queue/(what_you_want)" .只需sendTo(username, "/queue/(what_you_want)", msg)"/queue/(what_you_want)"

The bold statement came directly from RabbitMq website I tried to find the link but I got lazy大胆的声明直接来自RabbitMq网站我试图找到链接但我很懒惰

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM