简体   繁体   中英

How to send messages with selectors in websocket topic in Spring Boot

CLARIFICATION:

Thanks to @JustinBertram comment I realized that this question does not make sense.

STOMP protocol does not support selectors by itself, you have to use brokers such as ActiveMQ that implement it . STOMP supports headers that can be used by brokers for filtering messages by selectors.

In my case, I'm not using any broker, just frontend with Angular + Stomp + SocksJS and backend with Spring Boot, so I can't use selectors.

The documentation of STOMP protocol does not make this clear to me and I got confused. See these references:

the specification :

Stomp brokers may support the selector header which allows you to specify an SQL 92 selector on the message headers which acts as a filter for content based routing.

this article :

The subscribe() method takes an optional headers argument to specify additional headers when subscribing to a destination:

 var headers = {ack: 'client', 'selector': "location = 'Europe'"}; client.subscribe("/queue/test", message_callback, headers);

The client specifies that it will handle the message acknowledgement and is interested to receive only messages matching the selector location = 'Europe'.


I'm implementing a backend in Spring Boot. For two-way communications with the frontend I'm using stomp over websockets.

I have followed this Spring Boot + Angular example

It works, but one of my requirements is that the backend has to send messages with selectors so that the frontend subscribes to a topic and only receives the filtered data , to avoid performance issues with real time data.

ie { 'selector': "location = 'Europe'" }

For that purpose, I'm trying to make the backend send the messages with selectors, but I can't make it work.

I have followed this article to implement the frontend with selectors and it works correctly, the problem is only the backend .

I tried with @SendTo annotation but it seems it doesn't have any params for that as per the article:

@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message) throws Exception {
    Thread.sleep(1000);
    return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
}

Also I tried with the MessagingTemplate , but I don't know how to set the selector properties in the header:

http://assets.spring.io/wp/WebSocketBlogPost.html

MessageSendingOperations<String> messagingTemplate;

messagingTemplate.convertAndSend(destination, quote);

I really appreciate any help, I have read many articles and docs but I don't find anything talking in particular about this with a solution.

Well, it's possible to use the JMS' selectors with Spring (Boot) Websocket and a STOMP client. I found a native way.

The key thing is that the selector is applied to the org.springframework.messaging.Message instance , and it uses Spring's Spel language to apply the condition (it's not the JMS SQL-like).

So using the default SimpMessagingTemplate, in the backend you can send header variables like this:

this.messagingTemplate.convertAndSend(
    "/topic/something", //your destination
    payload, //any kind of payload (body)
    Map.of("id", 1) //header with key/value
);

In the frontend, to enter a selector that will be evaluated by the org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry.filterSubscriptions , you must declare your Stomp/WebSockets headers as:

{"selector": "headers['nativeHeaders']['id'][0] == '999'"}

Yeah, it's horrible but it works.

As the default Message is GenericMessage , the headers are processed in a new key called "nativeHeaders" . The ['key'],[0] and == are Spring's Spel sintaxes.

Go ahead and filter your messages on the backend, not in the frontend, please!

The latest version of the STOMP specification doesn't include any specific statement about selectors and their syntax because it's really up to the broker implementation as to what is supported here. The specification now just states:

STOMP servers MAY support additional server specific headers to customize the delivery semantics of the subscription. Consult your server's documentation for details.

Brokers like ActiveMQ 5.x and ActiveMQ Artemis support the selector STOMP header and the syntax & behavior of the selector is based on JMS selectors.

Selectors in JMS are for selecting messages on consumption and are configured by the consuming client. You can't set the selector when sending the message.

JMS selectors select messages based on the headers or properties of the message, although some implementations go beyond this and allow selecting based on the content of the message itself. Therefore, if you want to have a selector location = 'Europe' on a consumer then you should set a header on the message when it is sent with the name location and the value of Europe .

The convertAndSend method is overloaded and provides a couple of ways to set a header:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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