Keeping messages in queue in case of receiver crash - java

We've a Spring JMS message listener container for receiving messages asynchronously. Using DefaultMessageListenerContainer and in sessionTransacted mode. I understand being in sessionTransacted mode means in case of an exception the message will be put back into the queue. But how can I make sure the message won't be deleted from the queue even if the receiver (which is picked the message) crashes or just the machine running it looses power?
At first I thought CLIENT_ACKNOWLEDGE acknowledge mode should save me, but apparently it's not the case, Spring calls .acknowledge() no matter what.
So here's my question, how can I guarantee the delivery? Using a custom MessageListenerContainer? Using a transaction manager?

Use a transacted session and indicate successful message processing by invoking the Session class's commit() method.
Check the section 19.4.5. Processing messages within transactions for the configuration. (you can use a DefaultMessageListenerContainer). Depending on what you're doing with the messages, you may need a JTA transaction manager.

Spring message listener with Client_Acknowledge mode will acknowledge the message when the client calls message.acknowledge().
However, if after successful execution of the message, the consumer does not find any acknowledgement from the client side, spring assumes the execution was successful and acknowledges the message.
If at any point of time, consumer got an exception while processing the message, spring listener needs to know some exception has occured in order to redeliver the message to the queue for another consumer thread to pick it up. If you're catching the exception, spring assumes everything was handled and execution was smooth and hence acknowledges the message.
Spring message listener only allows to throw JMS exception from onMessage listener. Catching your custom exception and throwing a JMS exception from listener (after logging the error for future reference) will allow you to redeliver the message.

or you can use Session.AUTO_ACKNOWLEDGE with nontransacted session, see quote below from this article
A message is automatically
acknowledged when it successfully
returns from the receive() method. If
the receiver uses the MessageListener
interface, the message is
automatically acknowledged when it
successfully returns from the
onMessage() method. If a failure
occurs while executing the receive()
method or the onMessage() method, the
message is automatically redelivered.
The JMS provider carefully manages
message redelivery and guarantees
once-only delivery semantics.

Related

Spring AMQP : [RabbitTemplate] Hystrix fallback is not getting invoked when RabbitTemplate ReturnCallback is executed

I am using hystrix to handle falback scenarios when my messages are not delivered to RabbitMQ server. My fallback is getting invoked when RabbitMQ server is down(as AMQPException is thrown).
If broker is unable to accept/route the messages then returnCallback/returnConfirm(with nack) is invoked.
What I understand is that RabbitTemplate returnCallbacks/returnConfirms will be executed in different thread than the Hystrix thread.
Is it possible to throw Exception in these scenarios so that Hystrix fallback gets executed?
I have referred these q's : Spring AMQP return callback vs retry callback
Spring RabbitTemplate- How to get hold of the published message for NACKs in Publisher confirm mode
Any pointer to handle this scenario is much appreciated.
No; returns are completely asynchronous; even if you enable transactions - from the rabbit mq documentation...
AMQP does not specify when errors (e.g. lack of permissions, references to unknown exchanges) in transactional basic.publish and basic.ack commands should be detected. RabbitMQ performs the necessary checks immediately (rather than, say, at the time of commit), but note that both basic.publish and basic.ack are asynchronous commands so any errors will be reported back to the client asynchronously.
If you publish to a non-existent exchange (and setChannelTransacted(true)*) you will get an exception on the commit, but publishing to an exchange with no routable queue will never get an exception (only an async return callback).
enabling transactions is quite expensive for all operations so consider it carefully if you want to catch this particular scenario

How to Prevent Spring AMQP from Blocking on Unacked Messages?

I have a #RabbitListener annotated method for which Spring AMQP blocks after returning from the method. The underlying SimpleRabbitListenerContainerFactory uses AcknowledgeMode.MANUAL. I don’t want to acknowledge the message in the listener method, yet.
Is there any way to not have Spring AMQP block in such a scenario?
In more detail
I use a listener like this:
#RabbitListener(queues = "#{ #myQueue }")
void recordRequestsFromMyMessages(
#Payload MyMessage myMessagePayload,
#Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag,
Channel channel) {
// record relevant parts of the given message and combine them with
// parts from previous/future messages
// DON'T acknowledge the consumed message, yet; instead only keep a
// record of the channel and the delivery tag
}
Since I batch/combine multiple messages before I actually process them (asynchronously) later, I don’t want to acknowledge the consumed message right away. Instead, I only want to do this once the messages have been successfully processed later.
With my current approach, Spring AMQP blocks after returning from calling the recordRequestsFromMyMessages method above and no further messages are consumed from the same queue anymore.
This SO answer suggests that batch processing should work, however, I’m not sure how.
It's not the container that's "blocking".
You need to increase the prefetchCount on the container (default 1) - the broker only allows that number of unacked messages to be outstanding.

Can I receive a message, prepare its response and send a message in a context of a single JMS transaction?

Studying to a exam I just found a question I can't answer either after looking in the web. The question is:
"Can a server receive a request using a JMS message, prepare its response (e.g access a database) and send the reply using again, JMS, i the scope of a single JMS transaction?"
I know we can send a transacted message or receive a message in the context of a transaction. I know we can group several message sends and receives in a single transaction protecting the whole interaction. However, JMS is designed to be asynchronous. So in theory I would need to have a transaction to send the message to the queue and a transaction to receive the message from the queue. Am I right or is it possible to have a SINGLE transaction for a send and receive?
Yes, transacted receivers can be implemented in jMS. They are implemented by controlling the acknowledgemode of the communication: if all transactional operations are successful, the received message will be acknowledged to the broker, but in case of failure it does not happen, so the message can get redelivered.
This article explains this in more details:
Both message producers and message consumers may use transacted
sessions. [...]
With message consumers, transacted sessions control message
acknowledgment. The consumer can receive multiple messages just like
the CLIENT_ACKNOWLEDGE mode. When the associated transaction is
committed, the JMS implementation acknowledges all messages received
in the associated transaction. If the transaction aborts, the JMS
implementation returns the messages to the associated queue or topic.

Camel consume single message and stop, transacted

I am trying to use Camel to consume a single message from a JMS queue in a transacted manner. Specifically in a flow like this:
Wait until message is published on JMS queue
Try to consume and process the single message
If processing fails (exception occurs), rollback the consumption
If the processing passes, acknowledge and stop consuming anymore messages
Later in the application lifecycle, another process triggers consumption to start again from (1)
At first I tried to do this using a polling consumer, using the ConsumerTemplate, but I can't figure out if its possible to do this transactionally - it seems like the transaction is internal to the ConsumerTemplate so regardless of what I do the message is already acknowledged as consumed by the time the ConsumerTemplate returns.
Can I do this using the ConsumerTemplate? Can I do this using Camel and if so what is the best approach (Simple examples would be appreciated)?
I ended up using the pollEnrich dsl to achieve this. For example my route builder looks like:
from("direct:service-endpont").transacted("PROPOGATION_REQUIRED").setExchangePattern(ExchangePattern.InOut).pollEnrich("activemq:test-queue").bean(myHandler);
I use the direct endpoint as a service, sending a "request" message to the direct endpoint polls the jms queue for a single message (blocking if required). The transaction started extends to the pollEnrich so if, for example, myHandler bean fails then the message taken during the pollEnrich is not consumed and left on the queue.

Why DefaultMessageListenerContainer strongly recommends to use sessionTransacted

Javadoc for DefaultMessageListenerContainer says that
It is strongly recommended to either set "sessionTransacted" to "true"
or specify an external "transactionManager".
Why is it strongly recommended? What's behind this cryptic recommendation?
If your session is not transacted you risk message loss in the event your JVM goes down unexpectedly.
Here is the explanation of the various ack modes from AbstractMessageListenerContainer:
The listener container offers the following message acknowledgment
options:
"sessionAcknowledgeMode" set to "AUTO_ACKNOWLEDGE" (default): Automatic message acknowledgment before listener execution; no redelivery in case of exception thrown.
"sessionAcknowledgeMode" set to "CLIENT_ACKNOWLEDGE": Automatic message acknowledgment after successful listener execution; no redelivery in case of exception thrown.
"sessionAcknowledgeMode" set to "DUPS_OK_ACKNOWLEDGE": Lazy message acknowledgment during or after listener execution; potential redelivery in case of exception thrown.
"sessionTransacted" set to "true": Transactional acknowledgment after successful listener execution; guaranteed redelivery in case of exception thrown.
The exact behavior might vary according to the concrete listener
container and JMS provider used.
Because messaging is typically based on assured delivery, using the method that would offer the best safegaurds against message loss is recommended. You'd have to decide for a particular use case if its worth it.

Categories

Resources