ActiveMQ message lost when receiver is not available - java

I am using activeMQ for message passing. And when both sender and receiver available I successfully received the message. But when I down the receiver and start again queued messages are not received to receiver again. Is there any configuration I need to do?
I have increase the time to live as below to avoid expiring the session.
This is how my sender code looks like.
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createTopic(topic);
MessageProducer producer = session.createProducer(destination);
producer.setTimeToLive(18000000L);
TextMessage message = session.createTextMessage(customMessage.getContent());
producer.send(message);

What you're observing is the expected behavior. Since you're sending your message to a JMS topic you will be subject to publish/subscribe semantics. Publish/subscribe semantics dictate that messages are only placed in valid subscriptions. When your subscriber is offline it has no valid subscription on the broker to receive the messages therefore it will miss the messages sent to it when it is offline.
You can use a durable subscriber to receive messages when the subscriber is offline or you can use a traditional JMS queue.

Related

RabbitMQ not failing send when messages are rejected due to queue size

I am using RMQ and it's JMS client to publish messages to RMQ (this is a requirement i have, I can't use their java client instead of JMS client).
So, basically I do this:
RMQConnectionFactory factory = new RMQConnectionFactory() ;
factory.setUsername(props.getProperty("rmq.username"));
factory.setPassword(props.getProperty("rmq.password"));
factory.setHost(props.getProperty("rmq.host"));
factory.setVirtualHost(props.getProperty("rmq.virtualHost"));
factory.setPort(Integer.parseInt(props.getProperty("rmq.port")));
Connection connection = factory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
String queueName = managerProps.getProperty("rmq.queue.name");
Queue queue = session.createQueue(queueName);
producer = session.createProducer(queue);
TextMessage msg = session.createTextMessage(text);
msg.setText(text);
producer.send(msg);
I have a policy set up on RMQ overflow: reject-publish, so if it's over the limit RMQ is supposed to send a nack when the queue is full, but I don't seem to get it.
The question is - how do I determine if the message was rejected? I assume the producer.send(msg) to be synchronous and throw exception if the message is not published, but I don't get any exceptions, it just looks like everything got published.
JMS spec has a send(msg, CompletionListener) with a listener with two methods onCompletion and onException, but it doesn't look like RMQ JMS client implemented this method.
Is there another way to make sure that message made it through?
RabbitMQ use Publisher Confirms to guarantee that a message isn't lost, so if your Queue overflow behavior is reject-publish, the confirm channel will got a nack. It is also contains in many AMQP client.
But in JMS client, I have check the code in rabbitmq-jms-client, and no send implementaion contains CompletionListener. So if you want to enjoy reliable publish, please use AMQP client.
I did some digging, the CompletionListener is part of JMS 2.0 and RMQ only implements JMS 1.1, that's the reason it's not there.
But it looks like I can do something with transactions. I would need to change the code like this:
RMQConnectionFactory factory = new RMQConnectionFactory() ;
// ... skipping the code here
connection.start();
// set session to be transacted
session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
String queueName = managerProps.getProperty("rmq.queue.name");
Queue queue = session.createQueue(queueName);
producer = session.createProducer(queue);
TextMessage msg = session.createTextMessage(text);
msg.setText(text);
producer.send(msg);
// commit transaction
session.commit();
This will work if the queue is not full, but will throw an exception after a rejected message with this:
Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method(reply-code=406, reply-text=PRECONDITION_FAILED - partial tx completion, class-id=90, method-id=20)
I can then catch the exception and do what I need to do to resend/save the message.

What's the Purpose of setting the clientID for JMS publisher / consumer?

I understand that i need to set the clientId and subscription name when writing the jms topic subscriber for my durable topics.
But Whats the purpose of setting the clientID when publishing the TOPIC ? I have seen people setting the client Id even for publisher / consumer, but no one explained that why it is required.
ConnectionFactory conFactory = this.getConnectionFactory();
Connection connection = conFactory.createConnection();
connection.setClientID("WHATS_MY_PURPOSE"); // Why do we need clientID while publishing the TOPIC from consumer / publisher
connection.start();
MessageProducer producer = session.createProducer(destination);
A clientId is required to uniquely identify an application. It's a must when using a durable subscription in Pub/Sub messaging pattern. As you might be aware, a messaging provider caches publications destined for durable subscriber applications when they are off-line. When such applications come on-line again, a messaging provider has to identify OK, this is the same application that created a durable subscription but went away for reason. Now it has come back. So let me deliver all messages that were published when this application was away. To verify it's the same application, messaging provider compares the clientId of the application with clientId available with cached subscription information.

IBM MQ failure to send/receive all JMS Messages

I am using IBM MQ to produce messages while receiving it through a consumer on my client. To create the connection I'm using JmsConnectionFactory, along with provided properties to set up the connection with the server. So from what I understand is, as the consumer the only way to recognize the messages produced by the server is through the onMessage call. I'm currently testing this by creating a local producer and local consumer and assuring that every message sent by the producer is received by the consumer.
I'm running into the following problems:
I'm not receiving all messages produced.
Depending on the size of the message, more of them are received if they are smaller.
Here is code for the creation of the producer:
JmsConnectionFactory cf = ff.createConnectionFactory();
cf.setStringProperty(WMQConstants.WMQ_HOST_NAME, qm.getHost());
int port = ###;
cf.setIntProperty(WMQConstants.WMQ_PORT, port);
cf.setStringProperty(WMQConstants.WMQ_CHANNEL, qm.getChannel());
cf.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
cf.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, qm.getQueueManagerName());
Connection connection = cf.createConnection(qm.getUser().getUsername(), qm.getUser().getPassword());
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue(qm.getDestinationName());
LOG.debug("Destination Created at " +qm.getDestinationName());
msgSender = session.createProducer(destination);
msgSender.setDeliveryMode(DeliveryMode.PERSISTENT);
And this is how the producer is sending messages:
/**
* msgSender is the MessageProducer object
**/
private void produceMessages(int numOfMessages) throws JMSException, InterruptedException {
for (int i = 0; i < numOfMessages; i++) {
String text = "Message #" +i;
TextMessage message = session.createTextMessage(text);
msgSender.send(message);
}
}
On the consumer side, I am simply printing received messages and verifying visually:
#Override
public void onMessage(Message m) {
System.out.println(((TextMessage)m).getText());
}
I am not fully familiar with how IBM MQ works. Could the reason for the missing messages reside on the MQ simply ignoring messages that are produced before a message is fully sent?
I would say the issue is residing on your consumer side, rather than your simulated producer. Your message producer should be sending messages to MQ just fine, but multiple consumers are probably competing to retrieve these messages from the connection you have set up (given the same queue manager properties). So unless no one else is trying to consume from your IBM MQ, you're going to be expected to miss some messages.
You should use other method of send(Message m, CompletionListener l) to send new messages only after completion.
And if you use "Best Effort", it still will lose messages. You can try "Express" instead.

ActiveMQ Producer resend if destination not available

I am using the latest ActiveMQ with persistence and I have implemented already the producer and consumer which are working as intended. However I cannot get to producer to resend messages when the consumer is back from downtime. It only starts sending messages produces after the consumer is back up.
Producer
private void someInitFunction(){
connection = factory.createConnection();
connection.start();
session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
destination = session.createQueue(queueName);
producer = session.createProducer(destination);
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
}
private void onNewMessage(Serialized data){
try{
ObjectMessage message = createObjectMessage();
message.setObject(data);
producer.send(message);
} catch(Exception e){ log.error("consumer not available");}
}
What I want to achieve is to make the producer re-send the messages that failed to deliver while the consumer is down. So as an example: producer sends message 1,2 and consumer receives them but then it goes offline. Producer sends 3,4 while consumer is offline and the messages should be stored until consumer becomes available and 3,4 are resent for the consumer to receive.
I have read http://activemq.apache.org/how-do-durable-queues-and-topics-work.html and enabled durable queue using DeliveryMode.PERSISTENT but messages are not resent on-reconnect.
Any Ideas ?

ActiveMQConnectionFactory sendTimeout

According to this config page on the ActiveMQ site, the connection.sendTimeout property is:
Time to wait on Message Sends for a Response, default value of zero indicates to wait forever. Waiting forever allows the broker to have flow control over messages coming from this client if it is a fast producer or there is no consumer such that the broker would run out of memory if it did not slow down the producer. Does not affect Stomp clients as the sends are ack'd by the broker. (Since ActiveMQ-CPP 2.2.1)
I'm having difficulty interpreting what this means (and what the sendTimeout property really is/what it does):
What is a "Message Sends" object?
Why would ActiveMQ be waiting for a response? Isn't it on the server-side of a JMS connection? Shouldn't it be waiting for a request?
What does it actually timeout? When should it be used?
Thanks in advance!
The timeout affects the send of a Message by the client to the Broker. In the case where a send is not async then the client waits for the Broker to return a response indicating that the Message was received and added to the Message store. In some cases this can block for a long time if the Broker has engaged producer flow control because one of its preset memory limits has been reached. If the client application can't tolerate a long wait on send it could configure this timeout so that MessageProducer::send doesn't indefinitely block.
Messages are sent in synchronous mode either because the Connection was configured with alwaysSyncSend=true or because the MessageProducer is sending with the delivery mode set to Persistent.
In general this setting shouldn't need to be used if you've configured your Broker with limits that match your use case.

Categories