Getting duplicate ackMessage on ActiveMQ when using Mule Flows for connection - jms

When using mule flows as listed below, we are getting duplicate ackMessages on activeMQ. That is, the count of acknowledge messages is double the dispatch Messages.
<flow name="dequeueFlow" initialState="stopped">
<jms:inbound-endpoint queue="my.mq.queueName"
connector-ref="MyDequeueJmsConnector">
<jms:transaction action="ALWAYS_BEGIN"/>
</jms:inbound-endpoint>
<processor ref="MyRequestProcessor" />
</flow>
<jms:activemq-connector name="MyDequeueJmsConnector"
specification="1.1"
connectionFactory-ref="AmqConnectionFactory"
disableTemporaryReplyToDestinations="true"
persistentDelivery="true"
cacheJmsSessions="false"
numberOfConsumers="10"
acknowledgementMode="AUTO_ACKNOWLEDGE"
maxRedelivery="1">
</jms:activemq-connector>
In JmsConnector, the acknowledgementMode is set: acknowledgementMode="AUTO_ACKNOWLEDGE".
Instead of flows, if we are using default JMS connections, not seeing the issue. Any pointers on how this can be avoided?
Software Versions:
Mule Enterprise 3.4.1
ActiveMQ 5.9.0

Related

What is the correct endpoint of ObjectToJMSMessage?

I am new to Mule, and I really love the Anypoint Studio. I tried to put a message to ActiveMQ. I found that if I directly put JMS after string Payload, then it works, I can get the message in ActiveMQ. Like below:
But if I put an Object to JMSMessage transformer in it:
It keeps giving an error: java.lang.IllegalStateException: This transformer needs a valid endpoint. I almost tried all kinds of endpoint in, but no avail. I am wondering what should be the correct endpoint for the transformer?
The code is quite simple:
<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns:json="http://www.mulesoft.org/schema/mule/json" xmlns:wmq="http://www.mulesoft.org/schema/mule/ee/wmq" xmlns:db="http://www.mulesoft.org/schema/mule/db" xmlns:jms="http://www.mulesoft.org/schema/mule/jms" xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
xmlns:spring="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.mulesoft.org/schema/mule/jms http://www.mulesoft.org/schema/mule/jms/current/mule-jms.xsd
http://www.mulesoft.org/schema/mule/db http://www.mulesoft.org/schema/mule/db/current/mule-db.xsd
http://www.mulesoft.org/schema/mule/json http://www.mulesoft.org/schema/mule/json/current/mule-json.xsd
http://www.mulesoft.org/schema/mule/ee/wmq http://www.mulesoft.org/schema/mule/ee/wmq/current/mule-wmq-ee.xsd">
<http:listener-config name="HTTP_Listener_Configuration" host="localhost" port="8081" doc:name="HTTP Listener Configuration"/>
<jms:activemq-connector name="Active_MQ" brokerURL="tcp://localhost:61616" validateConnections="true" doc:name="Active MQ" specification="1.1" password="admin" username="admin"/>
<flow name="basic_tutorialFlow">
<http:listener config-ref="HTTP_Listener_Configuration" path="/" doc:name="HTTP"/>
<set-payload value="hello world" doc:name="Set Payload"/>
<jms:object-to-jmsmessage-transformer doc:name="Object to JMSMessage"/>
<jms:outbound-endpoint connector-ref="Active_MQ" doc:name="JMS" topic="mytopic"/>
<object-to-string-transformer doc:name="Object to String"/>
</flow>
</mule>
JMS When used in between flow elements acts as outbound end point (if you observe your xml it already has it "jms:outbound-endpoint" .What is means is it takes a payload publishes it to the Queue or Topic that you have configured the JMS Endpoint to ) .
Typically in your scenario you don't need to have a transformer before the JMS outbound end point Mule implicitly transforms the message for you . And as a consequence you can create a new flow or an application that is reading this Queue/Topic which is a JMS Inbound Endpoint (jms:inbound-endpoint), depending where you place the JMS Component mule determines wether it is inbound or outbound).
With this what you are achieving is the reliability pattern . For which you can read more about it here .
https://docs.mulesoft.com/mule-user-guide/v/3.8/reliability-patterns
You need to use the "Object to JMSMessage" transformer, and likewise "JMSMessage to Object" transformer, within the JMS component.
Example: I used the "Object to JMSMessage" transformer because I wanted my payload (java.util.Hashmap) to be transformed into a MapMessage, not a String. Intuitively, you should put the transformer before the JMS component.
In order to do this without getting the error java.lang.IllegalStateException: This transformer needs a valid endpoint, you have to find the Transformer settings within the JMS component, and add the transformer there.
So, for the JMS component that is writing messages to the queue, you add transformers to be referenced during Request:
When you click on the green plus-sign to add the transformer you want applied to the message before it's enqueued, you then select settings that show up when you use a normal transformer component within a Mulesoft flow.
In my example, I would enter in the Return Class as javax.jms.MapMessage.
Similarly, for a JMS component that's receiving messages from a JMS queue, you would add Transformers in the "Transformers Reference: Response" box, and put in the settings what class you would like the JMS message to be transformed into. This should work for any transformer that has "JMSMessage" in its name.
(Note: I am using Anypoint 6.6.3, Mule ESB 3.9.1, Mule 3, so this may not apply to Mule 4)

ActiveMQ network of brokers messages are not balanced

I am using ActiveMQ 5.13.4 and I have set up a network-of-brokers with two brokers A and B that are connected via a networkConnector. Each broker has a consumer connected to it (A' and B'). A' and B' consume messages from the same distributed queue (e.g. requests-queue). Each consumer is a java application, represented by a Spring DefaultJmsListenerContainerFactory having the setting concurrency: 5-10. Basically, each consumer has max 10 threads that can consume simultaneously messages from the queue.
In broker configuration (activemq.xml) I have set the prefetch size to 1, assuming that there are not many messages and they need a lot of processing time.
<policyEntry queue=">" producerFlowControl="true" optimizedDispatch="true" queuePrefetch="1" enableAudit="false">
The desired behavior is when I send 100 messages to A it starts processing 10 messages simultaneously (one for each consumer thread) and forwards another 10 to B. When B finishes processing the first 10 messages, it would get an extra 10. This would continue until all the messages are processed.
The actual behavior is that when I send 100 messages to A, it sends 10 messages, one per each thread, to the local consumer A' and forwards the remaining 90 to B'. The consumers on A' finish processing the messages and they wait while 90 messages get processed on broker B by its consumers.
Question: How can I configure the network in order to balance messages based on consumer availability?
The full activemq.xml configuration for one of the brokers is the following. The other one has the same configuration only a different name and IP in the networkConnector.
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd">
<!-- Allows us to use system properties as variables in this configuration file -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>file:${activemq.conf}/credentials.properties</value>
</property>
</bean>
<!-- Allows accessing the server log -->
<bean id="logQuery" class="io.fabric8.insight.log.log4j.Log4jLogQuery"
lazy-init="false" scope="singleton"
init-method="start" destroy-method="stop">
</bean>
<!--
The <broker> element is used to configure the ActiveMQ broker.
-->
<broker xmlns="http://activemq.apache.org/schema/core"
brokerName="broker-jms-tux-01-qa-gnd"
persistent="false"
dataDirectory="${activemq.data}"
cacheTempDestinations="false"
advisorySupport="true"
useJmx="true">
<destinationPolicy>
<policyMap>
<policyEntries>
<policyEntry topic=">" producerFlowControl="true">
<!-- The constantPendingMessageLimitStrategy is used to prevent
slow topic consumers to block producers and affect other consumers
by limiting the number of messages that are retained
For more information, see:
http://activemq.apache.org/slow-consumer-handling.html
-->
<pendingMessageLimitStrategy>
<constantPendingMessageLimitStrategy limit="1000"/>
</pendingMessageLimitStrategy>
<pendingSubscriberPolicy>
<vmCursor/>
</pendingSubscriberPolicy>
</policyEntry>
<policyEntry queue=">" producerFlowControl="true" optimizedDispatch="true" queuePrefetch="1" enableAudit="false">
<!--
Allow messages to be replayed back when the broker to which they were forwarded does not have any more consumers.
See section "Stuck Messages" from http://activemq.apache.org/networks-of-brokers.html
-->
<networkBridgeFilterFactory>
<!--
N.B.: When using replayWhenNoConsumers=true for versions < 5.9, it is necessary to also disable the cursors duplicate detection using enableAudit=false as the cursor could mark the replayed messages as duplicates (depending on the time window between playing and replaying these messages over the network bridge). The problem is fully explained here: http://tmielke.blogspot.de/2012/03/i-have-messages-on-queue-but-they-dont.html
-->
<conditionalNetworkBridgeFilterFactory replayWhenNoConsumers="true"/>
</networkBridgeFilterFactory>
<pendingQueuePolicy>
<vmQueueCursor/>
</pendingQueuePolicy>
<deadLetterStrategy>
<sharedDeadLetterStrategy expiration="300000"/>
</deadLetterStrategy>
</policyEntry>
</policyEntries>
</policyMap>
</destinationPolicy>
<!--
The managementContext is used to configure how ActiveMQ is exposed in
JMX. By default, ActiveMQ uses the MBean server that is started by
the JVM. For more information, see:
http://activemq.apache.org/jmx.html
-->
<managementContext>
<managementContext createConnector="true" connectorPort="1099"/>
</managementContext>
<!-- Network of brokers -->
<networkConnectors>
<networkConnector
name="broker-jms-tux-01-qa-gnd->broker-jms-tux-02-qa-gnd"
uri="static:(tcp://10.83.16.22:61616)"
conduitSubscriptions="false"
dynamicOnly="true"
networkTTL="2"
alwaysSyncSend="true"
decreaseNetworkConsumerPriority="true"
duplex="false"/>
</networkConnectors>
<!--
The systemUsage controls the maximum amount of space the broker will
use before disabling caching and/or slowing down producers. For more information, see:
http://activemq.apache.org/producer-flow-control.html
-->
<systemUsage>
<systemUsage>
<memoryUsage>
<memoryUsage percentOfJvmHeap="80"/>
</memoryUsage>
<storeUsage>
<storeUsage limit="10 gb"/>
</storeUsage>
<tempUsage>
<tempUsage limit="4 gb"/>
</tempUsage>
</systemUsage>
</systemUsage>
<!--
The transport connectors expose ActiveMQ over a given protocol to
clients and other brokers. For more information, see:
http://activemq.apache.org/configuring-transports.html
-->
<transportConnectors>
<!-- DOS protection, limit concurrent connections to 1000 and frame size to 400MB -->
<transportConnector name="openwire" uri="tcp://0.0.0.0:61616?maximumConnections=1000&wireFormat.maxFrameSize=3200000000"/>
</transportConnectors>
<!-- destroy the spring context on shutdown to stop jetty -->
<shutdownHooks>
<bean xmlns="http://www.springframework.org/schema/beans" class="org.apache.activemq.hooks.SpringContextHook"/>
</shutdownHooks>
</broker>
<!--
Enable web consoles, REST and Ajax APIs and demos
The web consoles requires by default login, you can disable this in the jetty.xml file
Take a look at ${ACTIVEMQ_HOME}/conf/jetty.xml for more details
-->
<import resource="jetty.xml"/>
</beans>
Thanks you in advance!
**Update 25/10/2016: **
I have come across the following statement in: http://activemq.apache.org/networks-of-brokers.html
The conditionalNetworkBridgeFilterFactory factory allows a rate limit
to be specified for a destination, such that network consumer can be
throttled. Prefetch for a network consumer is largely negated by the
fact that a network consumer relays a message typically acks very
quickly so even with a low prefetch and decreased priority a network
consumer can starve a modestly quick local consumer. Throttling
provides a remedy for this.
Question: It is still not clear for me, why when the local consumer A' finishes processing the initial 10 messages, broker A does not receive any of the 80 messages remaining on B (because 10 were dispatched to the local consumer of broker B)?
I have tried setting a higher networkTTL, but it still does not work.

JMS Queue not receiving messages with ActiveMQ

So I have used Spring integration to link JMS and ActiveMQ as under:-
<amq:broker useJmx="false" persistent="false">
<amq:transportConnectors>
<amq:transportConnector uri="tcp://localhost:0" />
</amq:transportConnectors>
</amq:broker>
<bean id="amqConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory" p:brokerURL="tcp://localhost:61616"/>
<bean id="destination" class="org.apache.activemq.command.ActiveMQQueue" c:name="destination"/>
<bean id="testTemplate" class="org.springframework.jms.core.JmsTemplate" depends-on="amqConnectionFactory" scope="prototype"
p:connectionFactory-ref="amqConnectionFactory"
p:pubSubDomain="false"
p:defaultDestination-ref="destination"
p:sessionTransacted="true"
p:receiveTimeout="5000"/>
Now, when I run the test by starting ActiveMQ and then running my application, I see that the messages are published to the "destination" queue from the logs. However, I am not able to retrieve any of these from the queue. Even in JConsole, it shows the queueSize and counts as 0.
How do I make it work so that I can send to and receives messages from this queue? Please help.
Why do you specify the transport connector to: tcp://localhost:0 and the ConnectionFactory URL to tcp://localhost:61616?
I would use the same URL for both.

One JMS Consumer stops listening active mq topic while second do not

A spring quartz process runs every 15 minutes in my project i.e 96 times a day. This fetch certain records from database and POST it on a REST service (running on JBoss 7). These records are in general 50 to 100 in count.
On REST service there is jms event publisher that publishes this message on a topic. There are two consumers on this topic.
That process message and sends push notification messages on mobile
Talk to third party (generally takes 4 to 5 second to complete the call)
Since it is topic both consumers receive all messages but they filter them out based on some property, so few messages are processed by one and rest by another consumer.
My problem is; which is being observed recently since a week time; that consumer #1 receives response from APNS as invalid token multiple times; token is used to send push notification to mobile; after some time this consumer stops and do not respond at all while second one keeps running.
Below are configurations:
<amq:broker id="broker" useJmx="false" persistent="false">
<amq:transportConnectors>
<amq:transportConnector uri="tcp://localhost:0"/>
</amq:transportConnectors>
</amq:broker>
<!-- ActiveMQ Destination -->
<amq:topic id="topicName" physicalName="topicPhysicalName"/>
<!-- JMS ConnectionFactory to use, configuring the embedded broker using XML -->
<amq:connectionFactory id="jmsFactory" brokerURL="vm://localhost"/>
<!-- JMS Producer Configuration -->
<bean id="jmsProducerConnectionFactory"
class="org.springframework.jms.connection.SingleConnectionFactory"
depends-on="broker"
p:targetConnectionFactory-ref="jmsFactory"/>
<!-- JMS Templates-->
<bean id="jmsTemplate"
class="org.springframework.jms.core.JmsTemplate"
p:connectionFactory-ref="jmsProducerConnectionFactory"/>
<!-- Publisher-->
<bean name="jmsEventPublisher"
class="com.jhi.mhm.services.event.jms.publisher.JMSEventPublisher">
<property name="jmsTemplate" ref="jmsTemplate"/>
<property name="topic">
<map>
<entry key="keyname" value-ref="topicName"/>
</map>
</property>
</bean>
<!-- JMS Consumer Configuration -->
<bean name="consumer2" class="Consumer2"/>
<bean name="consumer1" class="Consumer1"/>
<bean id="jmsConsumerConnectionFactory"
class="org.springframework.jms.connection.SingleConnectionFactory"
depends-on="broker"
p:targetConnectionFactory-ref="jmsFactory"/>
<jms:listener-container container-type="default"
connection-factory="jmsConsumerConnectionFactory"
acknowledge="auto"
destination-type="topic">
<jms:listener destination="topicPhysicalName" ref="consumer1"/>
<jms:listener destination="topicPhysicalName" ref="consumer2"/>
</jms:listener-container>
I search another posted questions but could not find anything related.
Your thoughts would be really helpful.
shailu - I went through similar problem. What we did is upgrade the version of MQ. Although this did not solved the problem completely as MQ shows random behavior and at the end we simply merged our endpoint and call destination as per biz logic

Mule 2.2.6 - strategies for handling JMS connection loss

I am using Mule 2.2.6, with the following setup:
2 servers, each with an instance of ActiveMQ
1 server running Mule 2.2.6
Now, I have a service that picks up a message from one ActiveMQ, and places it on the other. But, this is leading to problems with lost messages when the target ActiveMQ is down. Then, Mule will pick up a message from the source ActiveMQ, but run into this exception upon trying to place it on the destination AtiveMQ:
ERROR [org.mule.DefaultExceptionStrategy] Caught exception in Exception Strategy: No JMS Connection
java.lang.IllegalStateException: No JMS Connection
at org.mule.transport.jms.JmsMessageDispatcher.doDispatch(JmsMessageDispatcher.java:81)
at org.mule.transport.AbstractMessageDispatcher.dispatch(AbstractMessageDispatcher.java:105)
at org.mule.transport.AbstractConnector$DispatchWorker.doRun(AbstractConnector.java:2561)
at org.mule.work.AbstractMuleEventWork.run(AbstractMuleEventWork.java:41)
at org.mule.work.WorkerContext.run(WorkerContext.java:310)
at edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1061)
at edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:575)
at java.lang.Thread.run(Thread.java:662)
Upon this happening, the message is just lost, which is way below what is acceptable.
When examining the mule configuration, I noticed that there were no jms-transactions associaited with the service that moves messages from one ActiveMQ to the other, but after adding a transaction, the problem remained the same.
Configuration:
ActiveMQ connectors:
<jms:activemq-connector name="jmsConnectorOuter" specification="1.1"
persistentDelivery="true" disableTemporaryReplyToDestinations="true" honorQosHeaders="true"
numberOfConsumers="1" connectionFactory-ref="activeMqConnectionFactoryOuter" maxRedelivery="-1">
<ee:retry-forever-policy frequency="5000" asynchronous="false" />
</jms:activemq-connector>
<jms:activemq-connector name="jmsConnector" specification="1.1"
persistentDelivery="true" disableTemporaryReplyToDestinations="true" honorQosHeaders="true"
numberOfConsumers="1" connectionFactory-ref="activeMqConnectionFactory" maxRedelivery="-1">
<ee:retry-forever-policy frequency="5000" asynchronous="false" />
</jms:activemq-connector>
Jms-endpoints:
<endpoint name="queue.destination" address="jms://queue.destination" connector-ref="jmsConnector" />
<endpoint name="queue.source" address="jms://queue.source" connector-ref="jmsConnectorOuter" />
Service:
<service name="OuterActiveMQService">
<inbound>
<jms:inbound-endpoint ref="queue.source">
<jms:transaction action="ALWAYS_BEGIN" timeout="60000" />
</jms:inbound-endpoint>
</inbound>
<outbound>
<pass-through-router>
<jms:outbound-endpoint ref="queue.destination" />
</pass-through-router>
</outbound>
</service>
What can be done to make sure that messages aren't lost when the ActiveMQ connection to the destination is lost?
You need to add:
<jms:transaction action="ALWAYS_JOIN" />
in the outbound JMS endpoint to make it join the transaction.

Resources