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 ?
Related
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.
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.
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.
I have below pseudo code of flow which uses queue to send the message and then listen to topic synchronously. The underlying JMS provider is Tibco EMS.
//Send to Queue
Connection connection = createConnection(); // get the JMS connection
Session session = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue("sample.queue");
MessageProducer messageProducer = session.createProducer(queue);
Message message = createMessage(); //create JMS message
messageProducer.send(message);
Now, I listen to a topic using same session object created and wait till there is a response.
Topic topic = session.createTopic("sample.topic");
MessageConsumer messageConsumer = session.createConsumer(topic);
//wait for the reply.
Message responseMessage = messageConsumer.receive(60000);
if(responseMessage != null) {
System.out.println("Message received..");
}
The problem that I am facing is that the message object is coming out as null. I tested with a jms monitoring tool and the topic does have some message, but the above code is not able to pick it up even after 60 secs.
Any idea what am I missing here ?
Figured it out. Just before calling messageConsumer.receive();, a call to connection.start() to start the delivery of the message.
I'm learning how to use ActiveMQ and now we are facing the following problem.
Suppose that I have a topic named topic.test on ActiveMQ which have two subscribers.
In a given moment, I have only one of those subscribers waiting for messages, and a producer send a message for the topic I mentioned above.
Ok, the connected subscriber get the message, but shouldn't the other subscriber receive that message later when it is connected? Well, in my case it's not happening: my subscribers are only receiving messages while connected. All the other messages, which were sent while they were not connected are not being received by them. What could I be doing wrong?
Here is some of the source code I wrote to test ActiveMQ. Maybe you could find what is wrong with it.
My consummer code:
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
Connection connection = connectionFactory.createConnection();
connection.setClientID("leitorTeste");
conexao.start();
Session sessao = conexao.createSession(false, Session.AUTO_ACKNOWLEDGE);
Topic fonte = sessao.createTopic("topic.test");
MessageConsumer consumer = sessao.createConsumer(fonte);
javax.jms.Message presente = null;
while ((presente = consumer.receive()) != null) {
System.out.println(((TextMessage) presente).getText());
}
consumer.setMessageListener(new LeitorMensagens());
conexao.close();
And here is my producer code:
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
Connection connection = connectionFactory.createConnection();
Session sessao = conexao.createSession(true, Session.AUTO_ACKNOWLEDGE);
connection.start();
Destination destino = sessao.createTopic("topic.test");
MessageProducer produtorMensagem = sessao.createProducer(destino);
produtorMensagem.setDeliveryMode(DeliveryMode.PERSISTENT);
TextMessage message = sessao.createTextMessage("Hi!");
produtorMensagem.send(message);
sessao.commit();
connection.close();
Is there any other configuration I should add to ActiveMQ so that my consumers could get older messages?
You must make your consumers "permanent". Otherwise, AMQ "forgets" about them as soon as they unsubscribe. To do this, use Session.createDurableSubscriber()
There is something called a retroactive consumer policy you can also set on the broker. This is for Topic Subscribers - which aren't durable, but may wish to receive 'recent' messages they may have missed - see also Subscription Recovery Policy