Spring @SubscribeMapping是否真的向客户端订阅了某个主题? [英] Does Spring @SubscribeMapping really subscribe the client to some topic?

查看:986
本文介绍了Spring @SubscribeMapping是否真的向客户端订阅了某个主题?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在将Spring Websocket与STOMP(简单消息代理)一起使用. 在我的@Controller中,我使用方法级别的@SubscribeMapping,该方法应将客户端订阅一个主题,以便客户端以后可以接收该主题的消息.假设客户订阅了主题聊天" :

I am using Spring Websocket with STOMP, Simple Message Broker. In my @Controller I use method-level @SubscribeMapping, which should subscribe the client to a topic so that the client would receive the messages of that topic afterwards. Let say, the client subscribes to the topic "chat":

stompClient.subscribe('/app/chat', ...);

当客户端订阅"/app/chat "而不是"/topic/chat" 时,此订阅将转到使用@SubscribeMapping:

As the client subscribed to "/app/chat", instead of "/topic/chat", this subscription would go to the method which is mapped using @SubscribeMapping:

@SubscribeMapping("/chat")
public List getChatInit() {
    return Chat.getUsers();
}

这是Spring参考.说:

Here is what Spring ref. says:

默认情况下,发送@SubscribeMapping方法的返回值 作为消息直接返回到连接的客户端,并且不会通过 通过经纪人.这对于实现请求-回复很有用 消息交互;例如,当 应用程序用户界面正在初始化.

By default the return value from an @SubscribeMapping method is sent as a message directly back to the connected client and does not pass through the broker. This is useful for implementing request-reply message interactions; for example, to fetch application data when the application UI is being initialized.

好的,这就是我想要的,但是只是部分!订阅后发送一些初始化数据.但是订阅呢?在我看来,这里发生的事情只是请求-回复,就像服务一样. 订阅仅被使用.如果是这种情况,请告诉我.

Okay, this was what I would want, but just partially!! Sending some init-data after subscribing, well. But what about subscribing? It seems to me that the thing what happened here is just a request-reply, like a service. The subscription is just consumed. Please clarify me if this is the case.

  • 如果经纪人不参与,客户是否在某个地方订阅?
  • 如果以后我想向聊天"下标发送一些消息,客户端会收到吗?似乎并非如此.
  • 谁真正实现订阅?经纪人?还是其他人?

如果在这里没有在任何地方订阅客户端,我想知道为什么我们称其为订阅";因为客户端只收到一条消息,而不是将来的消息.

If here the client is not being subscribed to any where, I wonder why we call this as "subscribe"; because the client receives just one message and not future messages.

为确保已实现订阅,我尝试执行以下操作:

To make sure that the subscription has been realized, what I tried is as following:

服务器端:

配置:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/app");
    }

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

控制器:

@Controller
public class GreetingController {

    @MessageMapping("/hello")
    @SendTo("/topic/greetings")
    public Greeting greeting(HelloMessage message) throws Exception {
        System.out.println("inside greeting");
        return new Greeting("Hello, " + message.getName() + "!");
    }

    @SubscribeMapping("/topic/greetings")
    public Greeting try1() {
        System.out.println("inside TRY 1");
        return new Greeting("Hello, " + "TRY 1" + "!");
    }
}

客户端:

...
    stompClient.subscribe('/topic/greetings', function(greeting){
                        console.log('RECEIVED !!!');
                    });
    stompClient.send("/app/hello", {}, JSON.stringify({ 'name': name }));
...

我想发生的事情:

  1. 当客户端订阅"/topic/greetings"时,方法"try1"为 被执行.
  2. 当客户端将msg发送到'/app/hello'时,它应该收到问候消息msg,该问候语将为@SendTo'/topic/greetings'.
  1. When client subscribes to '/topic/greetings', the method try1 is executed.
  2. When the client sends msg to '/app/hello', it should receive the greetings msg which would be @SendTo '/topic/greetings'.

结果:

  1. 如果客户端订阅了/topic/greetings,则方法try1无法捕获它.

  1. If the client subscribes to /topic/greetings, the method try1 is UNABLE to catch it.

当客户端将msg发送到'/app/hello'时,执行greeting方法,并且客户端收到欢迎消息.因此,我们了解到它已经正确地订阅了"/topic/greetings".

When the client sends msg to '/app/hello', greeting method was executed, and the client received the greetings message. So we understood that it had been subscribed to '/topic/greetings' correctly.

但是请记住1.失败了.经过一番尝试后,客户端可以预订'/app/topic/greetings',即以/app为前缀(通过配置可以理解).

But remember 1. was failed. After some try, it has been possible when the client subscribed to '/app/topic/greetings', i.e. prefixed with /app (This is understandable by configuration).

现在1.正在工作,但是这次是2.失败:当客户端将msg发送到'/app/hello'时,是的,已执行greeting方法,但是客户端未收到问候消息. (因为可能现在客户端已订阅了以'/app'为前缀的主题,所以这是不需要的.)

Now 1. is working, however this time 2. is failed: When the client sends msg to '/app/hello', yes, greeting method was executed, but the client did NOT receive the greetings message. (Because probably now the client was subscribed to the topic prefixed with '/app', which was unwanted.)

所以,我得到的只是我想要的1个或2个,而不是这2个.

So, what I got is either 1 or 2 of what I would like, but not these 2 together.

  • 如何通过这种结构(正确配置映射路径)实现这一目标?

推荐答案

默认情况下,发送来自@SubscribeMapping方法的返回值 作为消息直接返回到连接的客户端,并且不通过 通过经纪人.

By default the return value from an @SubscribeMapping method is sent as a message directly back to the connected client and does not pass through the broker.

(重点是我的)

Spring Framework文档在这里描述了响应消息,而不是传入的SUBSCRIBE消息.

Here the Spring Framework documentation is describing what happens with the response message, not the incoming SUBSCRIBE message.

所以回答您的问题:

  • 是的,客户端已订阅该主题
  • 是的,如果您使用该主题发送主题,则订阅该主题的客户端将收到一条消息
  • 消息代理负责管理订阅

使用SimpleMessageBroker,消息代理实现位于您的应用程序实例中.订阅注册由DefaultSubscriptionRegistry管理. 接收消息时,SimpleBrokerMessageHandler处理SUBSCRIPTION消息并注册订阅(

With the SimpleMessageBroker, the message broker implementation lives in your application instance. Subscription registrations are managed by the DefaultSubscriptionRegistry. When receiving messages, the SimpleBrokerMessageHandler handles SUBSCRIPTION messages and register subscriptions (see implementation here).

使用RabbitMQ之类的真实"消息代理,您已经配置了一个Stomp代理中继,该代理将消息转发到代理.在这种情况下,SUBSCRIBE消息将转发给负责管理订阅的代理(

With a "real" message broker like RabbitMQ, you've configured a Stomp broker relay that forwards messages to the broker. In that case, the SUBSCRIBE messages are forwarded to the broker, in charge of managing subscriptions (see implementation here).

如果您查看

If you take a look at the reference documentation on STOMP message flow, you'll see that:

  • "/topic/greeting"的订阅通过"clientInboundChannel"传递并转发给经纪人
  • 发送到"/app/greeting"的问候通过"clientInboundChannel"传递,并转发给GreetingController.控制器添加当前时间,然后返回值通过消息"brokerChannel"传递到"/topic/greeting"(目的地是根据惯例选择的,但可以通过@SendTo覆盖).
  • Subscriptions to "/topic/greeting" pass through the "clientInboundChannel" and are forwarded to the broker
  • Greetings sent to "/app/greeting" pass through the "clientInboundChannel" and are forwarded to the GreetingController. The controller adds the current time, and the return value is passed through the "brokerChannel" as a message to "/topic/greeting" (destination is selected based on a convention but can be overridden via @SendTo).

因此,这里/topic/hello是代理目标;在那里发送的消息将直接转发给代理.尽管/app/hello是应用程序的目标,并且应该生成一条消息发送给/topic/hello,除非@SendTo另有说明.

So here, /topic/hello is a broker destination; messages sent there are directly forwarded to the broker. While /app/hello is an application destination, and is supposed to produce a message to be sent to /topic/hello, unless @SendTo says otherwise.

现在,您所更新的问题在某种程度上是一个不同的问题,并且如果没有更精确的用例,则很难说出哪种模式最能解决此问题.这里有一些:

Now your updated question is somehow a different one, and without a more precise use case it's difficult to say which pattern is the best to solve this. Here are a few:

  • 您希望客户端在发生任何事情时都能异步知道:订阅特定主题/topic/hello
  • 您要广播消息:将消息发送到特定主题/topic/hello
  • 您想获得某些事情的立即反馈,例如初始化应用程序的状态:订阅应用程序目标/app/hello,控制器立即响应消息
  • 您要将一个或多个消息发送到任何应用程序目标/app/hello:使用@MessageMapping@SendTo或消息传递模板的组合.
  • you want the client to be aware whenever something happens, asynchronously: SUBSCRIBE to a particular topic /topic/hello
  • you want to broadcast a message: send a message to a particular topic /topic/hello
  • you want to get immediate feedback for something, for example to initialize the state of your application: SUBSCRIBE to an application destination /app/hello with a Controller responding with a message right away
  • you want to send one or more messages to any application destination /app/hello: use a combination of @MessageMapping, @SendTo or a messaging template.

如果您想获得一个很好的例子,请查看此聊天应用程序,该日志演示了Spring websocket功能的日志一个真实的用例.

If you want a good example, then check out this chat application demonstrating a log of Spring websocket features with a real world use case.

这篇关于Spring @SubscribeMapping是否真的向客户端订阅了某个主题?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆