Message producer component has this :
session = connection.createSession(false,
Session.CLIENT_ACKNOWLEDGE);
producer = session.createProducer(session.createQueue(queueName));
I am using AWS SQS to send messages async
I was under the assumption that the message will be in the queue until it is acknowledged explicitly.(because of the client acknowledgement mode)
However it is removed off the queue.
Message consumer component:
I have written an async consumer to pull messages from the queue.
Here is the spring config file;
<bean id="ConnectionFactoryBuilder"
class="com.amazon.sqs.javamessaging.SQSConnectionFactory$Builder">
<property name="regionName" value="us-east-1" />
<property name="numberOfMessagesToPrefetch" value="1" />
<property name="awsCredentialsProvider" ref="CredentialsProviderBean" />
</bean>
<bean id="ConnectionFactory" class="com.amazon.sqs.javamessaging.SQSConnectionFactory"
factory-bean="ConnectionFactoryBuilder" factory-method="build" />
<bean id="Connection" class="javax.jms.Connection" factory-bean="ConnectionFactory"
factory-method="createConnection" init-method="start" destroy-method="close" />
<bean id="QueueName" class="java.lang.String">
<constructor-arg value="${sqs.queueName}" />
</bean>
<bean id="amazonMessageListener" class="com.myapp.AsyncMessageListener" />
<bean id="messageListener"
class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<property name="delegate" ref="amazonMessageListener" />
<property name="defaultListenerMethod" value="onMessage" />
<property name="messageConverter">
<null />
</property>
</bean>
<bean id="jmsContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="ConnectionFactory" />
<property name="destinationName" ref="QueueName" />
<property name="messageListener" ref="messageListener" />
<property name="sessionAcknowledgeMode" value="2"/>
</bean>
i configured my consumer with client acknowledgemode using the property sessionAcknowledgeMode. But still the message is removed off the queue without the consumer explicitly acknowledging using message.acknowledge()
Per AbstractMessageListenerContainer (super class of DefaultMessageListenerContainer) doc:
"sessionAcknowledgeMode" set to "CLIENT_ACKNOWLEDGE": Automatic message acknowledgment after successful listener execution; best-effort redelivery in case of a user exception thrown as well as in case of other listener execution interruptions (such as the JVM dying).
So when using DefaultMessageListenerContainer, if onMessage() method in your listener returns without exception, spring acknowledges the message automatically.
Related
I am using Jboss-fuse-6.3, with external Apache-activemq-5.15.2.
I have bind 50 consumers on a queue, and on Active MQ portal in "Active Consumers" page, I have noticed that all 50 consumers are bind but message distribution on queues are not the same.
Screen shot is attached. On session id "1" En-queued message count is around 1010 but on other consumer sessions En-queued messages are only 10.
Im en-queuing messages from Apache Camel Route. Below is my Blueprint xml (Am i doing some thing wrong)
<bean class="org.apache.activemq.spring.ActiveMQConnectionFactory" id="connectionFactory">
<property name="brokerURL" value="tcp://localhost:61616"/>
<property name="userName" value="admin"/>
<property name="password" value="admin"/>
<property name="trustAllPackages" value="true"/>
</bean>
<bean class="org.apache.camel.component.jms.JmsConfiguration" id="jmsConfig">
<property name="connectionFactory" ref="connectionFactory"/>
</bean>
<bean class="org.apache.activemq.camel.component.ActiveMQComponent" id="activemq">
<property name="configuration" ref="jmsConfig"/>
</bean>
<bean class="org.apache.activemq.spring.ActiveMQConnectionFactory" id="connectionFactory">
<property name="brokerURL" value="tcp://localhost:61616"/>
<property name="userName" value="admin"/>
<property name="password" value="admin"/>
<property name="trustAllPackages" value="true"/>
</bean>
<bean class="org.apache.camel.component.jms.JmsConfiguration" id="jmsConfig">
<property name="connectionFactory" ref="connectionFactory"/>
</bean>
<bean class="org.apache.activemq.camel.component.ActiveMQComponent" id="activemq">
<property name="configuration" ref="jmsConfig"/>
</bean>
<!-- ENQUEUEING MESSAGES -->
<to pattern="InOnly" uri="activemq:queue:MyQueue"/>
<!-- DEQUEUEING MESSAGES -->
<fromuri="activemq:queue:MyQueue?concurrentConsumers=50"/>
This appears to be normal prefetching behavior on the part of the JMS client. If you want competing consumers with fair dispatching then you need to lower the prefetch level as the first consumer that connects will often get a higher number of message dispatched to it because the default prefetch is 1000 for a Queue consumer.
Read more about ActiveMQ consumer prefetch here.
I use the Listener to read the messages from MQ as mentioned in the link here
ExampleMessageListener :
This implements the onMessage method of Interface javax.jms.MessageListener
Listener :
<bean id="messageListener" class="sample.ExampleMessageListener" />
<bean id="msgListenerContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="jmsConnectionFactory" />
<property name="destination" ref="jmsQueue" />
<property name="messageListener" ref="messageListener" />
</bean>
<jee:jndi-lookup id="jmsConnectionFactory" jndi-name="jms/CF1" />
<jee:jndi-lookup id="jmsQueue" jndi-name="jms/jmsQueue" />
I want to stop the listener when all the messages in MQ are read? (In other words, stop the listener when no meessages in MQ)
Any best way to identify the unavailability of messages in MQ?
using jms Browser ?
session.createBrowser(session.createQueue("Q"))
https://docs.oracle.com/javaee/7/api/javax/jms/Session.html#createBrowser-javax.jms.Queue-
https://docs.oracle.com/javaee/7/api/javax/jms/QueueBrowser.html#getEnumeration--
to start and stop the DMLC see here control-message-listener-container-to-stop-for-certain-period-and-start-again
I'm using ActiveMQ with the spring framework.
I have two consumers setup in the jms container. When I send 4 message to the queue, and some of the message are transferred to the "Dispatched Queue", because it takes a long time to the consumer to process the message.
I'm trying to find the way to prevent the message from going to the "Dispatched Queue", that is, I want them to be available to any consumer that is ready to consume the message.
I tried to set pre-fetch to 0, but it doesn't seem to work at all.
<bean id="prefetchPolicy" class="org.apache.activemq.ActiveMQPrefetchPolicy">
<property name="queuePrefetch" value="0"/>
</bean>
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<constructor-arg index="0" value="tcp://localhost:61616" />
<property name="prefetchPolicy" ref="prefetchPolicy"/>
</bean>
The following is the setup for my jms container:
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="defaultDestination" />
<property name="messageListener" ref="messageListener" />
<property name="concurrentConsumers" value="2" />
</bean>
I found the problem. I had the same beans declared twice at two different places. The second bean that was loaded did not have pre-fetch set to 0 and therefore it didn't work out.
The above setup I posted works!
Thank you!
I have a component which reads messages from a queue and meanwhile sends the processed messages to another queue. Therefore, this component is both a message consumer and producer. For configuring them, I need a connection factory for consuming and another connection factory for the producing. Here is part of the spring configuration.
<!-- Configuration for listener -->
<bean id="mdc.TargetConnectionFactory4Listener" class="com.tibco.tibjms.TibjmsConnectionFactory">
<property name="serverUrl" value="tcp://localhost:7222"/>
</bean>
<bean id="mdc.ConnectionFactory4Listener" class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter">
<property name="targetConnectionFactory" ref="mdc.TargetConnectionFactory"/>
<property name="username" value="admin" />
<property name="password" value="test" />
</bean>
<bean id="mdc.InputQueue" class="com.tibco.tibjms.TibjmsQueue">
<constructor-arg>
<value>INPUT_QUEUE</value>
</constructor-arg>
</bean>
<bean id="mdc.JmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="mdc.ConnectionFactory4Listener" />
<property name="destination" ref="mdc.InputQueue" />
<property name="messageListener" ref="mdc.MessageReceiver" />
......
</bean>
<!-- Configuration for sender -->
<bean id="mdc.TargetConnectionFactory4Sender" class="com.tibco.tibjms.TibjmsQueue">
<property name="serverUrl" value="tcp://localhost:7222"/>
</bean>
<bean id="mdc.CachingConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="mdc.TargetConnectionFactory4Sender" />
<property name="sessionCacheSize" value="50" />
</bean>
<bean id="mdc.OutputQueue" class="com.tibco.tibjms.TibjmsQueue">
<constructor-arg>
<value>DISCOVERY_QUEUE</value>
</constructor-arg>
</bean>
<bean id="mdc.JmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="mdc.CachingConnectionFactory" />
</bean>
<bean id="mdc.MessageReceiver" class="net.siemens.discovery.queue.QueueMessageListener">
<property name="jmsTemplate" ref="mdc.JmsTemplate" />
<property name="destination" ref="mdc.OutputQueue" />
......
</bean>
The two queues are running on the same EMS server. Some has opinions about this configurations: they can be configured by using only one ConnectionFactory and two instances are not necessary. However, if I use one ConnectionFactory instance, then the instance is used both in the DefaultMessageListenerContainer and the CachingConnectionFactory (further used in JmsTemplate). I don't know whether they have impact on each other.
It is perfectly normal to use a single connection factory; it is very unusual to use 2 factories in this case.
In fact, if you want to perform JmsTemplate operations on the container thread and you want the interactions to run in a transaction (sessionTransacted = true in the container), then you must use the same connection factory. This allows everything to roll back if there's an exception.
When using a caching connection factory in the listener container you should set the connection factory cacheConsumers to false. (See this answer for more information.
I have written a small test where I send a message to an existing ActiveMQ FORWARD queue. Unfortunately the message is sent to the queue but not received. Below you will find my two attempts to receive this message: through a MyMessageListener and through a MessageService. Both methods fail.
Here is my test:
Map<Parameter, String> params = new HashMap<Parameter, String>();
params.put(key1, "601");
params.put(key2, "3000");
Map<String,String> headers = Collections.singletonMap("method-name","prepareHotDrink");
Message<?> msg = MessageBuilder.withPayload(params)
.copyHeaders(headers)
.build();
boolean i = inputChannel.send(msg);
This is my configuration file:
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="${jms.primary.server}"/>
</bean>
<!-- spring integration beans -->
<int-jms:channel id="inputChannel" queue-name="FORWARD"
connection-factory="connectionFactory" auto-startup="true">
</int-jms:channel>
<!-- Consumers -->
<int-jms:message-driven-channel-adapter
id="jmsIn"
container="messageListenerContainer"
channel="inputChannel"
extract-payload="true" />
<int:service-activator input-channel="inputChannel"
ref="messageService"
method="processMessage"/>
<bean id="messageListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="requestQueue"/>
<property name="exposeListenerSession" value="false"/>
<property name="messageListener" ref="messageListener" />
</bean>
<bean id="messageListener" class="com.ucware.ucpo.forward.mess.MyMessageListener"/>
<bean id="messageService" class="com.ucware.ucpo.forward.jms.MessageService"/>
<bean id="requestQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg name="name" value="FORWARD"/>
</bean>
It turned out ActiveMQ does not support channels. So I had to define a <jms:lintener-container> and <jms:listener> to be able to consume the messages. In addition I created a message producer that sends messages to the existing ActiveMQ queue.
<!-- RECEIVER -->
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory" p:brokerURL="${jms.primary.server}"/>
<bean id="messageListener" class="com.ucware.ucpo.forward.jms.ProductMessageListener"/>
<jms:listener-container connection-factory="connectionFactory" concurrency="2" acknowledge="auto">
<jms:listener destination="FORWARD" ref="messageListener" method="onMessage"/>
</jms:listener-container>
<!-- SENDER -->
<!-- A cached connection to wrap the ActiveMQ connection -->
<bean id="cachedConnectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory"
p:targetConnectionFactory-ref="connectionFactory"
p:sessionCacheSize="10" />
<!-- A destination in ActiveMQ -->
<bean id="destination"
class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="FORWARD" />
</bean>
<!-- A JmsTemplate instance that uses the cached connection and destination -->
<bean id="producerTemplate"
class="org.springframework.jms.core.JmsTemplate"
p:connectionFactory-ref="cachedConnectionFactory"
p:defaultDestination-ref="destination" />
</beans>