当前位置: 首页 > 知识库问答 >
问题:

spring@subscribeMapping真的会让客户端订阅某个主题吗?

危钱明
2023-03-14

我正在使用spring Websocket与STOMP,简单的消息代理。在我的@controller中,我使用方法级的@subscribeMapping,它应该为客户端订阅一个主题,以便客户端在之后接收该主题的消息。假设客户端订阅主题“聊天”:

stompclient.subscribe(“/app/chat”,...);

当客户端订阅了“/app/chat”而不是“/topic/chat”时,此订阅将转到使用@subscribemapping映射的方法:

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

这是spring裁判。说道:

默认情况下,来自@SubscribeMapping方法的返回值作为消息直接发送回连接的客户端,而不通过代理。这对于实现请求-回复消息交互很有用;例如,在初始化应用程序UI时获取应用程序数据。

好吧,这是我想要的,但只是部分!!订阅后发送一些init数据。但是订阅呢?在我看来,这里发生的事情只是一个请求-回复,就像一个服务。订阅已被使用。如果是这样,请澄清我。

  • 如果经纪人没有参与其中,客户是否订阅了某些where?
  • 如果稍后我要向“聊天”订阅者发送一些消息,客户端会接收到吗?看来不是这样。
  • 谁真正实现订阅?经纪人?还是别人?

如果这里的客户机没有被订阅到任何地方,我想知道为什么我们称之为“subscribe”;因为客户端只接收一条消息,而不接收将来的消息。

编辑:

为了确保订阅已经实现,我尝试了如下内容:

服务器端:

配置:

@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. 当客户端将消息发送到“/app/hellow”时,它应接收到问候消息,该消息将为@sendto/topic/greetings”。

结果:

>

  • 如果客户端订阅/topic/greetings,则方法try1无法捕获它。

    当客户端将消息发送到“/app/hello”时,greeting方法被执行,客户端收到了问候消息。所以我们知道它已经正确地订阅了'/topic/greetings'。

    但请记住1。失败了。经过一些尝试,当客户端订阅'/app/topic/greetings'时,这是可能的,即前缀为/app(通过配置可以理解这一点)。

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

    所以,我得到的是我想要的一个或两个,但不是这两个加在一起。

    • 如何使用此结构(正确配置映射路径)实现此目标?
  • 共有2个答案

    罗均
    2023-03-14

    所以两者兼备:

    • 使用主题处理订阅
    • 在该主题上使用@subscribemapping传递连接响应

    并不像你(和我)经历的那样起作用。

    解决你的情况(就像我的情况一样)的方法是:

    >

  • 删除@subscribeMapping-它仅适用于/app前缀
  • 订阅/topic,就像您想要的那样(带/o/app前缀)
  • 实现ApplicationListener

    >

  • 如果您想直接回复单个客户端,请使用用户目的地(请参阅websocket-stomp-user-destination或者您也可以订阅子路径,例如/topic/my-id-42,然后您可以向该子主题发送消息(我不知道您的确切用例,我的用例是我有专用的订阅,如果我想进行广播,我会迭代它们)

    收到StompCommand.Subscribe后,立即在ApplicationListener的onApplicationEvent方法中发送消息

    订阅事件处理程序:

    @Override
      public void onApplicationEvent(SessionSubscribeEvent event) {
          Message<byte[]> message = event.getMessage();
          StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
          StompCommand command = accessor.getCommand();
          if (command.equals(StompCommand.SUBSCRIBE)) {
              String sessionId = accessor.getSessionId();
              String stompSubscriptionId = accessor.getSubscriptionId();
              String destination = accessor.getDestination();
              // Handle subscription event here
              // e.g. send welcome message to *destination*
           }
      }
    

  • 单淳
    2023-03-14

    默认情况下,来自@SubscribeMapping方法的返回值作为消息直接发送回连接的客户端,而不通过代理。

    (重点是地雷)

    这里,spring框架文档描述的是响应消息发生的事情,而不是传入的subscribe消息。

    所以回答你的问题:

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

    使用SimpleMessageBroker,message broker实现位于应用程序实例中。订阅注册由DefaultSubscriptionRegistry管理。接收消息时,SimpleBrokerMessageHandler处理订阅消息和注册订阅(请参见此处的实现)。

    对于RabbitMQ这样的“真正的”消息代理,您已经配置了一个Stomp broker中继,它将消息转发给代理。在这种情况下,subscribe消息被转发给负责管理订阅的代理(参见此处的实现)。

    如果您查看有关STOMP消息流的参考文档,您将看到:

    • 对“/topic/greeting”的订阅通过“ClientInboundChannel”并转发到代理
    • 发送到“/app/greeting”的问候语通过“ClientInboundChannel”并转发到GreetingController。控制器添加当前时间,返回值通过“BrokerChannel”作为消息传递到“/topic/greeting”(目的地是根据约定选择的,但可以通过@sendto覆盖)。

    因此在这里,/topic/hello是一个代理目标;在那里发送的消息直接转发给代理。而/app/hello是应用程序目标,并且应该生成要发送到/topic/hello的消息,除非@sendto另有规定。

    现在您更新的问题是一个不同的问题,如果没有一个更精确的用例,很难说哪种模式是解决这个问题的最佳模式。这里有几个:

    • 您希望客户端在异步情况下知道任何时候发生的事情:订阅特定主题/topic/hello
    • 要广播消息:向特定主题发送消息/topic/hello
    • 您希望立即获得某项内容的反馈,例如初始化应用程序的状态:订阅应用程序目标/app/hello,控制器立即响应消息
    • 要将一个或多个消息发送到任何应用程序目标/app/hello:请使用@messagemapping@sendto或消息传递模板的组合。

    如果您想要一个好的例子,那么请查看这个聊天应用程序,该应用程序演示了spring websocket功能的日志,并使用了一个真实的用例。

     类似资料:
    • 客户对主题的订阅(即订阅者)的寿命是多少? 我之所以这么担心,是因为我认为服务总线的一个特点--持久的信息。因此,我认为在连接不稳定的情况下,可以保证提供持久的消息。 那么,当一个应用程序(多个应用程序中的一个)失去与服务总线的连接一天,然后第二天重新启动应用程序并实例化一个新的订阅客户端实例时,会发生什么呢?当其他应用程序由于自己的订阅已经处理了这些相同的消息时,应用程序是否会继续接收等待传递的

    • 我一直在研究一种方法来限制可以订阅特定跺脚主题但尚未理解的客户数量,这可能是根据我的需求的正确方法。 我的用例是一个游戏,我正在Angular(ng2 stompjs stomp客户端)和Spring Boot Websockets(目前正在使用Spring内存消息代理)中开发它。 其想法是,用户可以连接并订阅“/大厅”跺脚主题,在那里他可以看到打开的游戏室,这些游戏室可能处于不同的状态。例如,由

    • 问题内容: 试图创建两个客户;一个是pub / sub,另一个是标准连接。这不可能吗?必须有一种将其抽象化的方法才能起作用:)基本上,如果我在运行test.js之后执行了一个操作,则看到的只是’valueBefore’。输出: 代码: 问题答案: 您可能需要发布来自的消息,因为它专门用于侦听某些频道上的消息。在node_redis自述文件中几乎没有关于此行为的文字: 如果在发布/订阅模式下需要向R

    • 我有一个websocket服务器和一个websocket客户端,都是Java的。websocket服务器具有以下功能: 在 Java 网页滑板客户端中,我在我的踩踏会话处理程序中提供了以下内容: 然后,我能够通过客户端向服务器路径“hello”发送消息来在两者之间进行通信,然后由于客户端订阅了“topic/greetings”,所以我也要用我的stompFrameHandler来处理响应。 但是我

    • https://github.com/azure/azure-service-bus/tree/master/samples/dotnet/gettingstart/microsoft.azure.servicebus/topicsubscriptionwithruleoperationssample 现在我想添加一个筛选器/规则,这样只有通过筛选器中定义的特定条件的消息才应该给订阅。 例如,下面

    • 我正在试验消息驱动Beans,以便从外部ActiveMQ实例接收主题订阅消息。 我的测试首先从队列订阅开始,它工作得很好。 然后我想尝试主题订阅,但我无法让它工作。 这就是我所拥有的: 会议记录。xml 这是MDB: 我不知道为什么,但从日志中我可以看到,TomEE创建了一个队列,而不是一个主题: 另一个证明是,当我添加持续时间配置时,服务器不会启动: 然后服务器抱怨这不适合配置类型javax.j