I am using Spring stomp websocket framework to send subscription messages to the clients. We are using ActiveMQ as the message broker
and is using a stomp javascript client. Spring 4.1.5 and In this architecture, the messages are sent using
simplemessagingTemplate.convertAndSendToUser(user, "/queue/msg", serverMsg, map);
In order to ensure that only the right user receive their message, I am also making use of a QueueSubscriptionInterceptor that implements ChannelInterceptor.
The messages are delivered to the destination correctly. The messages are received using the JS client like this.
stompClient.subscribe('/user/guest/queue/msg', function(greeting){
x = JSON.parse(greeting.body);
...
}
So far so good. However, when there are multiple user session, only one session receives the message. For (e.g), if two "guest" users
are logged in, I would like all the two "guest" users to receive the message. This doesnt seem to be happening. Looking into the logs,
I see that the message seems to be sent..
2015-04-11 14:39:40 DEBUG StompBrokerRelayMessageHandler:738 - Forwarding SEND /queue/msg-user1 session=_system_ application/json;charset=UTF-8 payload={"my message to you...)
2015-04-11 14:39:40 DEBUG StompBrokerRelayMessageHandler:738 - Forwarding SEND /queue/msg-user0 session=_system_ application/json;charset=UTF-8 payload={"my message to you...)
I see only one client receiving the message and not the other. Reading the Spring documentation, I understand that this is the default behaviour.
Can someone tell me what I am doing wrong.
Thanks.
You should use the semantic of topics instead of queues.
A queue allows the message to be consumed once, but a topic will allow it to be consumed once per subscriber. So something like /topic/user/guest/msg probably would do it.
Set the ack header in the connect frame. What this will do is that the server will continue to send you the same message until you send back an ack frame. Which I am doing below by calling greeting.ack() as soon as receive the message. Setting it to 'client-individual' will mean that the server has to receive ack from all sessions of the particular client or it will keep re sending the same msg on every CONNECT.
Hope this helps!!
Use below code for reference.
function connect() {
var socket = new SockJS('/powerme-notification-websocket');
stompClient = Stomp.over(socket);
stompClient.connect(
{client_id:'testClient'}, function (frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/user/topic/releaseLock', function (greeting) {
stompClient.subscribe('/queue/releaseLock-testClient', function (greeting) {
console.log(greeting);
showGreeting(greeting.body);
greeting.ack();
},{durable:true,
'auto-delete':false,
ack:'client-individual',
id:'testClient'});
});
}
References:
https://stomp.github.io/stomp-specification-1.2.html#ACK
Related
I have a stomp application and I set the ack mode to client during subscription from front-end. During the subscription the same client during a session would connect to the same queue in the message broker ActiveMQ Artemis because a session id is used for queue.But a different id is passed in the header every time the user reloads the page.
The subscription code is shown below.
consumer.subscribe(
'/queue.'+ rscSessionId,
function (response) {
console.log("resposne");
},
{
"subscription-type": "ANYCAST",
ack: "client",
id: generateId(),
}
);
Once I send message from client it gets send to frontend with following headers.
MESSAGE
subscription:1234
message-id:1543449
destination:/queue.sessionid
expires:1653914606725
redelivered:false
priority:4
persistent:true
timestamp:1653912806717
destination-type:ANYCAST
__AMQ_CID:30e64793-dd9b-11ec-8843-c238460c3152
_type:ResponseData
content-length:300
and afterwards on every reload.
MESSAGE
subscription:1235
message-id:1543449
destination:/queue.sessionid
expires:1653914606725
redelivered:true
priority:4
persistent:true
timestamp:1653912806717
destination-type:ANYCAST
__AMQ_CID:30e64793-dd9b-11ec-8843-c238460c3152
_type:ResponseData
content-length:300
You can see that redelivered is set to true after first delivery and after that every response has redelivered to false. the subscription value keeps getting changed though because of the id passed in header.
How can I make it deliver only once but with guarantee?
Are you actually acknowledging the message once you receive it? If you don't acknowledge the message then once the client closes the connection and subscribes again it will receive the same message again. The different value you're seeing for the subscription header indicates this is what is happening.
To be clear, if you're using the client acknowledgement mode then once you receive the MESSAGE frame you need to send a corresponding ACK frame in order to acknowledge the message. Your client should have a way to do this since it is a standard part of the STOMP specification.
I guess that Artemis is configured with publisher/subscriber messaging style. In this case, every new consumer/receiver/client will get the messages aka you write/read from a topic.
Changing to a point-to-point message style would fix the issue (would only be read once) aka a queue.
This is usually decided by the setup but I recall that ActiveMQ allowed producer to create those on the fly.
If you do need a topic, then you will have to handle the IDs on the client side.
I have a doubt, in a group chat system that has a database with a rest API, who should issue the event of a new message?
The client or the endpoint to create the new message?
For example: X user sends a message to group Y, then uses the api endpoint api.com/message-create and that endpoint emits the message-create event through websocket
Example 2: X user sends a message to group Y, then uses the api api.com/message-create endpoint and the endpoint does not emit the message-create event, but was emitted when the user pressed the send message button
I still don't quite understand if it would occupy more websocket channels to achieve that, if a global one is enough, etc.
The server should be responsible for communication logic. So your first example is better.
But: why do you use two communication channels for sending an creating messages?
If you use websocket, you don't need create a message from a client by using additional rest endpoint.
This approach is prone to errors. For example if the client will loose network connection after sending message through websocket and before executing call to the REST endpoint?
The message will not be stored in a database.
Your flow should looks as follows:
User clicks the send button.
Message is send through the websocket.
Message is stored in the database asynchronously (you can do it directly from communication server, or use rest endpoint)
Emit "new message" event to the group.
I would like to implement an request/response pattern in the following parts:
Server: Springboot with ActiveMQ
Client: JavaScript with stompjs over websocket
I want a behaviour like an http request.
Send a Request, get a corresponding Response.
I try to do this with temporary channels
I send 3 Messages
The steps are:
SUBSCRIBE to a temprorary channel
SUBSCRIBE
id:mysub
destination:/temp-queue/example
SEND the request and include a reply-to header with the subscribed channel of Step 1
destination:/queue/PO.REQUEST
reply-to:/temp-queue/example
Get the Response Message from the Server
MESSAGE
subscription:foo
reply-to:/queue/temp.default.23.example
destination:/queue/PO.REQUEST
reply-to:/temp-queue/example
But now (As Client send messages asynchronous) im not sure if Step 1 is complete on server, and so server is ready to send Response to the queue when the Request of Step 2 arrives at the server.
Is it possible that server finishes Step 2 before finishing Step 1, and so sends the response to nowhere? Or does ActiveMQ ensures that the received messages 1 and 2 from the client are processed and finished in the correct order?
Can any race condition between message 1 and 2 happen?
Thank you very much!
Any STOMP frame that your client sends can be sent with a receipt request that makes the processing of that frame synchronous. So if you want to ensure that a subscribe is complete before doing the send, then attach a receipt-id to the subscribe frame and wait for the spec mandated RECEIPT frame before going on to do the send, this ensures that your subscription is setup prior to any other processing.
I am using Autobahn and I have an implementation-specific question.
I am trying to figure out how to send a notice to all connected clients (including the newly subscribed client) upon a client subscribing to a topic. Here's the code (edited down for clarity):
#exportSub("", True)
def subscribe(self, topicUriPrefix, topicUriSuffix):
topic_uri = "%s%s" % (topicUriPrefix, topicUriSuffix)
self.client.dispatch(topic_uri, {"msg":"WTF"})
return True
Yet, I'm not seeing the newly subscribed message receive this dispatch. The dispatch call is returning None.
What's happening here?
I figured this out. A client must first be subscribed to a topic before receiving a message sent via dispatch(). This means that the dispatch() cannot be called inside subscribe if one expect the subscribing client to receive the message. I worked around this problem by building a simple message queue and calling dispatch on the protocol instance for any queued messages.
After successfully sent the URI to the web service from the push client, I send a toast notification from web service by using the URI, in web service I get the response as :
Push status 200,
NotificationStatus : Received,
DeviceConnectionStatus : Connected,
NotificationChannelStatus : Active.
But no message is received in the push client. The same scenario used to work fine earlier today. Can anyone tell me what is going wrong?
Is the message you are sending the same (ie. identical)? I seem to remember that some instances of a malformed message would get through the service OK, but then be suppressed on the device.
Do you have code to handle toast messages that arrive while your app is running? If you do, put a breakpoint in there and send a toast to the app while you're debugging and see what comes out. In this way you can make sure that the toast is making it to the device, and also see what the content is or what the problem might be.
channel.ShellToastNotificationReceived += channel_ShellToastNotificationReceived;
where channel is your channel object, and then
void channel_ShellToastNotificationReceived(object sender, NotificationEventArgs e)
{
Dispatcher.BeginInvoke(() => MessageBox.Show(e.Collection["wp:Text1"] + Environment.NewLine + e.Collection["wp:Text2"]));
}
or something similar to pop the message out to the display.
In web service, if a wrong setRequestProperty is set for example, setting wrong X-WindowsPhone-Target and wrong X-NotificationClass, then web service will receive notification received status, but the push client will not receive any message.
In my case I was sending a toast message with X-WindowsPhone-Target as token and X-NotificationClass as 1. After giving correct value it has started working fine.