I am using Spring's DefaultMessageListenerContainer to receive messages from an ActiveMQ queue. This application is the sole producer of messages to this queue. The application is clustered so in the case that I have two instances (instance1, instance2) how can I ensure that messages produced and sent to the queue from instance1 are received by the message listener on instance1 and messages produced and sent to the queue from instance2 are received by the message listener on instance2? My initial though was to set the messageSelector property of the DefaultMessageListenerContainer, but I'd like each application to have the same configuration.
You can't do that; you either need a separate queue for each instance or use a message selector.
Related
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.
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.
We have an EIP flow which uses annotation-based spring:4.2.x APIs, spring-integration:4.2.x APIs and spring-integration-java-dsl:1.1.0 APIs to receive messages from a Websphere MQ Queue, do some processing and finally return responses to another Websphere MQ Queue. For this flow, we are using a JMS Inbound Gateway to synchronously receive messages from one queue, process them and send the responses back to another queue.
The JMS Inbound Gateway is configured with an errorChannel so that RuntimeExceptions are routed to it (this works fine). However during tests, when we purposely apply a PUT_INHIBIT on the flow's response Websphere MQ queue (i.e. to cause the flow to be unable to send responses back to the reply queue), the spring logs show the following WARNING log message:
WARN ... - Execution of JMS message listener failed, and no ErrorHandler has been set.
javax.jms.JMSException: MQJMS2007: failed to send message to MQ queue.
We know we can remove that WARNING log by configuring an ErrorHandler on the MLC itself but, the reason this is causing us problems is that when we route a response back, we actually route using a .routeToRecipients() call with .setIgnoreFailures(false) and two recipients - the first recipient routing to the JMS Inbound Gateway's replyChannel and the second routing to a post-send flow so that we can do DB updates, etc. The idea here being that if the first recipient send fails (i.e. when the response queue is not available), the post-send flow doesn't execute but instead an error handling flow executes instead (e.g. errorChannel flow). But in the described error scenario, we see the warning log, and the flow's post-send flow still executes instead of the errorChannel's flow...
It is as though, at this point, the JMS Inbound Gateway's errorChannel no longer applies. Is this correct? And is this intended behaviour? And if it is, does this mean that we should use Inbound/Outbound Adapters instead of an Inbound Gateway for our response post-send intent?
JMS MLC Configuration:
#Bean( destroyMethod = "shutdown")
public DefaultMessageListenerContainer serviceMLC() throws Exception {
DefaultMessageListenerContainer mlc = new DefaultMessageListenerContainer();
mlc.setAutoStartup(false);
mlc.setConnectionFactory(serviceCCF);
mlc.setDestination(requestMqQueue);
mlc.setAcceptMessagesWhileStopping(false);
return mlc;
}
JMS Inbound Gateway Configuration:
#Bean
public IntegrationFlow serviceFlow() {
return IntegrationFlows
.from(Jms
.inboundGateway(serviceMLC)
.autoStartup(true)
.defaultReplyDestination(responseMqQueue)
.replyChannel(responseOutCh)
.replyTimeout(180000)
.correlationKey("JMSCorrelationID")
.errorChannel(serviceErrorCh)
)
.channel(serviceInCh)
.get();
}
Yes; the gateway doesn't work that way.
When you send the reply to the gateway, it is queued in the gateway until the thread returns to the gateway; at which time, the reply is picked up and sent. So, the failure to send does not occur until later (after your second recipient flow is invoked).
Yes, to do what you want, you should use channel adapters instead because the failure will run directly on the calling thread.
I have a rabbitmq listener as a separate class and JSF 2 managed bean.
In my bean I send a message and need to wait for result. I can't use sendAndReceive... because I send the message to one queue but receive from another queue, so I assign correlationId before sending.
So I need to wait asynchronously, I need to wait until right message comes to the listener. How to do it in rmq?
Looking at javadoc and source of RabbitTemplate it seems that he waits for response in reply queue. Do you set 'reply-to' property in your messages? If yes, then RabbitTemplate sendAndReceive methods should wait for response in 'reply-to' queue. Be sure to populate replyTo field correctly and test it.
Side note:
In RabbitMQ you do not send messages to the queue.
You send messages to the exchanges. Exchanges are routing messages to the queue(s) using bindings. With default or direct exchange type it looks like you send directly to the queue, but this is over-simplification.
See https://www.rabbitmq.com/tutorials/amqp-concepts.html for details.
Edit:
It seems there are some fix for that in AMQP 1.4.5.RELEASE
https://spring.io/blog/2015/05/08/spring-amqp-1-4-5-release-and-1-5-0-m1-available
Configurable Exchange/Routing Key for Replies
Previously, when using request/reply messaging with the
RabbitTemplate, replies were routed to the default exchange and routed
with the queue name. It is now possible to supply a reply-address with
the form exchange/routingKey to route using a specific exchange and
routing key.
I am new to JMS. As far as I understood Consumers are capable of picking messages from queue/topic. So why do you need a MessageListener because Consumers will know when they have picked up messages? What is the practical use of such a MessageListener?
Edit:From the Javadoc of MessageListener:
A MessageListener object is used to receive asynchronously delivered
messages.
Each session must insure that it passes messages serially to the
listener. This means that a listener assigned to one or more consumers
of the same session can assume that the onMessage method is not called
with the next message until the session has completed the last call.
So I am confused between the usage of the terms asynchronously and serially together. How do these two terms relate in describing the feature of MessageListener?
The difference is that MessageConsumer is used to receive messages synchronously:
MessageConsumer mc = s.createConsumer(queue);
Message msg = mc.receive();
For asynchronous delivery, we can register a MessageListener object with a message consumer:
mc.setMessageListener(new MessageListener() {
public void onMessage(Message msg) {
...
}
});
from the docs:
For synchronous receipt, a client can request the next message from a message consumer using one of its receive methods.
For asynchronous delivery, a client can register a MessageListener object with a message consumer.
One major difference as per my knowledge not stated in others answers is that MessageConsumer can make use of MessageSelectors and hence has capability to consume messages that it's interested in, where as MessageListener will listen to all messages.
From the J2EE tutorial doc http://docs.oracle.com/javaee/5/tutorial/doc/bnceh.html
JMS Message Selectors
If your messaging application needs to filter the messages it receives, you can use a JMS API message selector, which allows a message consumer to specify the messages it is interested in. Message selectors assign the work of filtering messages to the JMS provider rather than to the application.