I have a question on how JMS is supposed to be used. Here's my case:
I have a queue with multiple consumers
A message gets sent to the queue - f.e. a "login" message
One of the consumers processes the message
Now I want to tell all my systems about the "login" message - i.e. that the user successfully logged in. What I'm currently doing is:
The consumer that processed the message sends a message to a Topic where everybody listens telling them "User x successfully logged in". Let's call this SUCCESS.
Now every system concerned knows that "user x has successfully logged in" due to the SUCCESS message. This is what I want.
However, if I understood JMS message delivery rules right, it is theoretically possible that a message to another topic/queue that relies on the fact that the receiving consumer knows that "user x logged in" could arrive before my SUCCESS message has been received. Even if it was sent after the session.send() call of the SUCCESS message. Is that right?
If so, how are you supposed to implement such a case with JMS?
Any help would be greatly appreciated!
Is that right?
Unfortunately, yes.
If so, how are you supposed to implement such a case with JMS?
Two different approaches come to my mind:
simulate other network protocols - add ACKNOWLEDGE message that every system must sent when it receives SUCCESS message. ACKNOWLEDGE message would be sent to some dedicated topic, and messages that rely on the fact the receiving consumer knows that user x logged in cannot be sent until ACKNOWLEDGE message arrived from that consumer.
send both SUCCESS and further messages on same topic (if that's applicable; other consumers can ignore further messages if they aren't final destination), and give greater priority to SUCCESS message. That should (at least theoretically - JMS API doesn't require this!) guarantee that SUCCESS message arrived prior to messages that rely on the fact the receiving consumer knows that user x logged in. The method that should interest you in this case is Message#setJMSPriority
Related
I have 2 systems where System A has to send messages to System B. I am new to JMS so I don't have a big idea on how to implement this. I was thinking of using a message broker (ActiveMQ) to send messages. So, System A will send messages to a queue and the Message Listener in B will consume those messages. There are many users in System B and I want these messages to be shown whenever the user log into the system. So my problem is, if System B keeps consuming messages even when the users are not logged in how can they see the messages which are already consumed?
Should I store the consumed messages in a database? I don't understand how this works.
You could let SystemB read all the messages and act as a gateway between JMS and the system itself, i.e. store each user message in the database and when a user logs in, read those messages from the database and display them. If the user has to acknowledge they've read each message that might be a better solution as you can then track if they've read them all and delete each one from the database as they acknowledge they've read it.
Another solution might be virtual topics and queues. A single topic that messages are sent to that is split into queues, one per user. When a user logs in, SystemB reads from that user's queue. This is separate from the application's domain (it's JMS at this point) so the message is marked as consumed and is taken off the queue by ActiveMQ. If the user doesn't read it and needs to see it the next time they login then you need the database solution.
It's essentially where two domains meet. The information the user needs to see comes in on JMS, which has its own rules (message consumed, remove from queue etc). The information then enters your application's domain which might have different rules (must read, save for next login etc).
If a user doesn't login for a long period of time, their queue might fill up and not be able to receive any more messages, whereas, if the messages are always read and stored in a database it doesn't matter how frequently they login as the database should be better at holding large amounts of messages.
Another option is one topic per user and messages are sent to those topics but other systems would need to know which users are in your system, which probably isn't a good idea. Or you could use Apache Camel to route incoming messages on the main topic to user topics. The messages would need to be durable and transacted in case the broker went down. When a user logs in, read from the topic to get all their messages. You can route based on content or headers.
Your problem is one of message persistence and or re-delivery. There are a couple of approaches:
JMS Durable subscription: you could make a durable subscription on a topic from system B and only consume messages while users are logged in. When you don't receive() messages, your messages will be held at the broker for you until you call receive() again. In case A sent messages persistently, all of this is saved by the ActiveMQ broker on disk.
JMS Queue: system A puts messages into a queue and System B doesn't pick up the messages unless users are logged in. The queue will get bigger until you call receive() again from system B. Similar to durable subscriptions, but with a queue you can only have one consumer for each message. With durable subscriptions it's a easier to configure a fault-tolerant version of system B...
Add a 'replay server' for n-times delivery: system A publishes to a topic (could be non-persistent) and a third component (C) would also subscribe to every message and persist to disk. When system B needs to see a message again, it could ask system C for those messages, ideally supporting from_time, or similar.
I have a HornetQ based JMS provider and the consumer is attached to the provider in the CLIENT_ACKNOWLEDGE_MODE . The message.acknowledge () snippet though is under an if else . Thus the consumer would sometimes would not send an acknowledgement to the server in case there was an application layer failure in processing the message .So there are two questions here -
1)Will more messages that are queued in the server will keep on
flowing to the consumer even though the consumer did not acknowledge
on of the messages as stated earlier
2)Will the unacknowledged message flow down again on restarting the
consumer .
These are some of my observations on the questions I have asked
1)The messages keep on flowing down to the consumer as per the
consumer logs even though it did not acknowledge one of the messages
to the server due to an application layer failure (Note , there was
no uncaught exception as such , just that the consumer did not
acknowledge) .
2)Secondly , on restarting the consumer as well the message did not
flow down again from the server which is surprising .
Can someone please clarify this behaviour?
It has hard to fully determine what your application is doing in terms of message acknowledgement but my guess is that you are continuing to acknowledge messages after the failed attempt to acknowledge the message in question. In that case because you are using the Client Acknowledge mode the next Acknowledge will also apply to the previous message as that is how client mode works.
Session.CLIENT_ACKNOWLEDGE: A client acknowledges a message by calling the message’s acknowledge method. In this mode, acknowledgment takes place on the session level: Acknowledging a consumed message automatically acknowledges the receipt of all messages that have been consumed by its session. For example, if a message consumer consumes ten messages and then acknowledges the fifth message delivered, all ten messages are acknowledged.
So if you read that carefully you will see how consumers in a Session acknowledging messages while in client Acknowledge mode could even affect one another.
For a better answer you'd need to break down the chain of event further so that in became more clear what is going on.
We have a JMS queue and multiple competing clients are reading from this queue.
Once the message is taken and successfully processed, we want to send the acknowledge to delete ( i.e. CLIENT ACKNOWLEDGE )
However, we want to make sure that if one client has picked the message another client should not take it from the queue.
Does activeMQ provide this feature out of the box using some configuration ?
Moreover:
If the message processing failed after picking the message, so it could not be acknowledged back, in this scenario we should like other client thread to pickup the message. Is it possible out of the box with configuration , may be specifying timeout values ?
Regards,
JE
You need to take some time to understand the difference between a Topic and a Qeueue in order to understand why the first question is not an issue.
For the second question it depends a bit on the ACK mode you are using and how you are processing messages sync or async. Normally for processing where you want to control redeliveries you would do the work inside of a transaction and if the processing fails the message would be redelivered when the TX is rolled back. ActiveMQ supports redelivery policies both client side and broker side that control how many time a message will be redelivered before sent to a DLQ.
What is the best practice to know if a message that is sent via jmsTemplate.send(new MessageCreator()....
I have an async process where a message is delivered to a queue, then some other third party process picks up the message.
What is best practice to know if the message was delivered, or in a case such as this is it normal to only check for errors?
My program will be receiving messages rather slowly; and I want to them to persist in the queue until I have receive all of them and acknowledge all of them. I don't know if I have enough messages until I receive a bunch of them.
My question: will the queue block, waiting for the acknowledgement from the first message before delivering the second?
Well I ran a test one this using the sample producer/consumer code. The consumer actually has some code (if you switch over to ClientAcknowledge). It receives a bunch of messages (10 of them) and only acks the last one.
When setting the acknowledge mode to Session.CLIENT_ACKNOWLEDGE you can get as many messages you need. The messages will be locked on the server, so no other consumer can retrieve them meanwhile. So the answer is no, the queue won't block (even thu there might be provider-specific settings that can do that, which I don't know).
However, you can acknowledge only all at once. So when you have received 10 messages, and you acknowledge one of them (doesn't matter which), all messages will be acknowledged.
Check for your reference Controlling Message Acknowledgment