我正在使用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数据。但是订阅呢?在我看来,这里发生的事情只是一个请求-回复,就像一个服务。订阅已被使用。如果是这样,请澄清我。
如果这里的客户机没有被订阅到任何地方,我想知道为什么我们称之为“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 }));
...
我希望发生的事情:
/topic/greetings
”时,将执行方法try1
。/app/hellow
”时,它应接收到问候消息,该消息将为@sendto
“/topic/greetings
”。结果:
>
如果客户端订阅/topic/greetings
,则方法try1
无法捕获它。
当客户端将消息发送到“/app/hello
”时,greeting
方法被执行,客户端收到了问候消息。所以我们知道它已经正确地订阅了'/topic/greetings
'。
但请记住1。失败了。经过一些尝试,当客户端订阅'/app/topic/greetings'
时,这是可能的,即前缀为/app
(通过配置可以理解这一点)。
现在是1。正在工作,不过这次2。失败:当客户端向“/app/hello
”发送消息时,是,执行了greeting
方法,但客户端未接收到问候消息。(因为现在客户端可能订阅了前缀为“/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*
}
}
默认情况下,来自@SubscribeMapping方法的返回值作为消息直接发送回连接的客户端,而不通过代理。
(重点是地雷)
这里,spring框架文档描述的是响应消息发生的事情,而不是传入的subscribe
消息。
所以回答你的问题:
使用SimpleMessageBroker
,message broker实现位于应用程序实例中。订阅注册由DefaultSubscriptionRegistry
管理。接收消息时,SimpleBrokerMessageHandler
处理订阅
消息和注册订阅(请参见此处的实现)。
对于RabbitMQ这样的“真正的”消息代理,您已经配置了一个Stomp broker中继,它将消息转发给代理。在这种情况下,subscribe
消息被转发给负责管理订阅的代理(参见此处的实现)。
如果您查看有关STOMP消息流的参考文档,您将看到:
因此在这里,/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