I have a situation in which the I'm reading messages from a queue using a message consumer (javax.jms.MessageConsumer) .
The session used is using AUTO_ACKNOWLEDGE mode.
From what I've read so far on AUTO_ACK mode:
In auto acknowledgement if the consumer's onMessage() method completes without error the message is considered received and processed successfully, it'll be then removed from the JMS server.
My question is when is the message considered to be ACK by the JMS producer considering I`m not using an MDB that has an onMessage() method but reading the messages by using the message consumer described earlier.
Is the message ACK'ed once I successfully read it using the messageConsumer ?
What will happen if further down the logic-chain a method that uses the respective message will throw an error ? Will the message already be ACK'ed by that time ?
The Javadoc for the AUTO_ACKNOWLEDGE constant says this:
With this acknowledgment mode, the session automatically acknowledges
a client's receipt of a message either when the session has
successfully returned from a call to receive or when the message
listener the session has called to process the message successfully
returns.
I suspect you are calling receive on the MessageConsumer (although you don't explicitly state that) so if you set AUTO_ACKNOWLEDGE it is acknowledged by the time receive returns.
Of course if you have a transacted session then the acknowledge mode is ignored and the message isn't considered received until the session is committed.
Related
I am using Amazon SQS with Amazon SQS-JMS java library with Java EE 7. What I want to achieve is after receiving a message, depending on business logic of the application either confirm (consume) the message or resend it to the queue again and after 3 failed retries move it to DLQ.
I though about using CLIENT_Acknowledge mode in JMS and only acknowledging the messages that were successfully processed, but this is from their official documentation:
In this mode, when a message is acknowledged, all messages received before this message are implicitly acknowledged as well. For example, if 10 messages are received, and only the 10th message is acknowledged (in the order the messages are received), then all of the previous nine messages are also acknowledged.
This example also seems to confirm this: http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/code-examples.html#example-synchronous-receiver-client-acknowledge-mode.
For me this is kind of a strange behavior and opposite what I would expect from a client_acknowledge. Is there a more elegant solution here than just manually sending message throughout the code to main SQS queue or DLQ depending on process status?
You can use:
UNORDERED_ACKNOWLEDGE
SQSSession.UNORDERED_ACKNOWLEDGE
Which comes from 'com.amazon.sqs.javamessaging;' and as it states in the documentation it is a variation of Client_Acknowledge which only acknowledges the message for which it is called.
/**
* Non standard acknowledge mode. This is a variation of CLIENT_ACKNOWLEDGE
* where Clients need to remember to call acknowledge on message. Difference
* is that calling acknowledge on a message only acknowledge the message
* being called.
*/
dependency example:
"com.amazonaws:amazon-sqs-java-messaging-lib:1.0.3"
To handle this case you can use RedrivePolicy attribute for the DLQ that you created. Solution for this case can be:
Create a 2 sqs Qs say my_q and my_q_dl (latter one is for DLQ)
Set DLQ my_q_dl as the DLQ of my_q by using RedrivePolicy.
Here, care should be taken to specify deadLetterTargetArn and maxReceiveCount. This maxReceiveCount is the number of times you want to process any message without acknowledging before sending it to the DLQ. If you set maxReceiveCount=3 then, the msg will remain in my_q up to 3rd pull by the consumer with no ack.
2 cases here:
Normal case: msg gets deleted as soon as ack is received.
If no ack (msg delete) for that msg upto third time then the msg gets deleted from my_q and pushed to
my_q_dl itself.
*RedrivePolicy - The string that includes the parameters for the deadletter queue functionality of the source queue.
deadLetterTargetArn - The Amazon Resource Name (ARN) of the dead-letter queue to which Amazon SQS moves messages after the value
of maxReceiveCount is exceeded.
maxReceiveCount - The number of times a message is delivered to the source queue before being moved to the dead-letter queue.
Note
The dead-letter queue of a FIFO queue must also be a FIFO queue. Similarly, the dead-letter queue of a standard queue must also be a
standard queue.*
In jms (v1.0) subscriber client ack mode, message.acknowledge() is the only way to send an ack back to server(broker) side. The actual behaviour is if client ack to message3 then message sender(broker) client acks to all messages up to message3[1].
i.e
msg1, msg2, msg3 is delivered to the client in order.
Client process messages ACK for every message, msg1, msg2 and msg3. >> OK
Knowing the actual behaviour client acks as batches(batch size=3), so asks to msg3 > OK (all messages upto msg3 get acked)
In scenario 1 and 2, broker get notified that client ACK to all 3 messages delivered. And client also actually process all 3 and ACK back.
Consider following scenario :
a. msg1 comes to client.
b. failed to process msg1. So avoid ack back. (msg1 never processed or acked from client side)
c. msg2 comes and successfully processed. And ack to msg2.
Therefore in the above scenario client ack does not guarantee the delivery of msg1.
Please explain is there a workaround to guaranteed delivery when batch process with client ack through JMS 1.0 spec.
[1] http://docs.oracle.com/javaee/7/api/javax/jms/Message.html#acknowledge
According to the spec:
By invoking acknowledge on a consumed message, a client acknowledges all messages consumed by the session that the message was delivered to.
So, the deal is to not acknowledge any more messages in a session where a message has failed.
Instead, if you detect a failure, you could either:
Tear down the session (or even Connection).
Invoke recover on your Session. That will restart message delivery with the oldest unacknowledged message.
Is there any way a publisher can be acknowledged that a published message has been delivered to a listener when using Spring AQMP? I have a number of queues where I set x-message-ttl = 0, which means messages will be discarded if they cannot be immediately delivered, but as I'm using this in a request/reply scenario, I'd like to be able to abort the request and handle an error immediately.
You could publish a message with the mandatory flag.
If this flag is set, the server will return an undeliverable message
with a Return method. If this flag is zero, the server will queue the
message, but with no guarantee that it will ever be consumed.
And set a return callback which will be called if the message in unroutable.
Another solution should be to use an alternate exchange associated to your exchange. The cons are that you need to bind a queue to this AE and consume messages to be able to know if a request has failed.
I am trying to understand these two API's : setJMSRedelivered and getJMSRedelivered for my project.
Link : http://docs.oracle.com/javaee/5/api/javax/jms/Message.html#getJMSRedelivered()
Steps used :
Call setJMSRedelivered(true) on message we want to publish on a topic.
publish the message.
Consume the message. Call getJMSRedelivered() on message.
It should receive true. But its retuning false.
Am I missing something?
No, you are no supposed to set the JMSRedelivered property while publishing a message. This property will be set by the messaging provider when it delivers a message more than once.
According to JMS specification
If a client receives a message with the JMSRedelivered indicator set, it is likely, but not guaranteed, that this message was delivered but not acknowledged in the past. In general, a provider must set the JMSRedelivered message header field of a message whenever it is redelivering a message. If the field is set to true, it is an indication to the consuming application that the message may have been delivered in the past and that the application should take extra precautions to prevent duplicate processing.
This header field has no meaning on send and is left unassigned by the
sending method.
Read carefully the last line, it says JMSRedelivered has no meaning when a message is sent.
In your case, since the message has not been redelivered, the getJMSRedelivered property call is returning FALSE.
Update
Sample code to test JMSRedelivered property.
// Create JMS objects
connection = cf.createConnection();
System.out.println("Created connection to " + queueManager);
// Create a transacted session.
session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
System.out.println("Session created");
Destination topicScore = session.createTopic("/SCORE");
MessageConsumer consScore = session.createConsumer(topicScore );
JMSBytesMessage msg=null;
// Receive message first
msg = (JMSBytesMessage) colesConsumer.receiveNoWait();
System.out.println(msg.getJMSRedelivered);
// Rollback the previous receive, to force messaging provider to redeliver the message
session.rollback();
// receive message again
msg = (JMSBytesMessage) colesConsumer.receiveNoWait();
// This time JMSRedelivered will be true.
System.out.println(msg.getJMSRedelivered);
Hope this helps to you.
setJMSredelivered is managed by the container, if you set it to true and publish the message [which has not been 'redelivered'] the container sets setJMSredelivered to false.
When the message is delivered, and, for example, something fatal happens, the container will rollback the call and sets setJMSredelivered to true.
I have a question :
Is this correct, as I am not able to find the same anywhere in java docs ?
From here JavaWorld
In AUTO_ACKNOWLEDGEMENT mode(non-transactional)
If a failure occurs while executing the receive()[synchronous] method or the onMessage()[aysnc] method, the message is automatically redelivered
I think that if we got a message in onMessage it means the message is successfully delivered to the user. JMS provider must make sure that no messages are lost. onMessage can only wait for the next successfully delivered message, it cannot know about problems between JMS provider and JMS server.