Spring

Resolving Durable Duplicate Client ID Issues

Once I completed the previous blog post: Spring and WebLogic Durable Subscriber, the configurations were deployed to a multiple domain cluster (with some Managed Servers running on a separate physical machines). Errors started to occur within the cluster based on the use of the SingleConnectionFactory configuration. What appeared to be happening was when the subscribe war was deployed out to WebLogic Server, the 1st Managed Server started and created a durable session with the configured clientId. The problem was when the war started on the 2nd Managed Server, it attempted to create a durable session with that same configured clientId and we started receiving errors such as this:

[JMSClientExceptions:055083]Cannot set clientid to sampleSubscribeService. The client ID is already set to sampleSubscribeService
2012-12-28 12:40:12,604 THREAD:[143] WARN ID:[] org.springframework.jms.listener.DefaultMessageListenerContainer – Could not refresh JMS Connection for destination ‘jmsServer1@com/foo/dest/SampleTopic’ – retrying in 3600000 ms. Cause: [JMSClientExceptions:055083]Cannot set clientid to sampleSubscribeService. The client ID is already set to sampleSubscribeService

The following configuration existed (as shown in the other blog) for the subscribe service:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<bean id="sampleDurableTopicConnectionFactory"
    class="org.springframework.jms.connection.SingleConnectionFactory"
    p:targetConnectionFactory-ref="sampleMessagingConnectionFactory"
    p:clientId="sampleSubscribeService"/>

<bean id="sampleSubscribeServiceListenerContainer"  
    class="org.springframework.jms.listener.DefaultMessageListenerContainer"
    p:connectionFactory-ref="sampleDurableTopicConnectionFactory"
    p:destinationName="jmsServer1@com/foo/dest/SampleTopic"
    p:destinationResolver-ref="sampleDestinationResolver"
    p:messageListener-ref="sampleSubscribeServiceMessageListener"
    p:pubSubDomain="true"
    p:subscriptionDurable="true"
    p:durableSubscriptionName="sampleSubscribeService"
    p:transactionManager-ref="GlobalDataTransactionManager"/>

This included the configuration for the WebLogic Server ConnectionFactory:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<connection-factory name="SampleConnectionFactory">
    <default-targeting-enabled>true</default-targeting-enabled>
    <jndi-name>com/foo/SampleConnectionFactory</jndi-name>
    <client-params>
        <client-id-policy>Restricted</client-id-policy>
        <subscription-sharing-policy>Exclusive</subscription-sharing-policy>
        <messages-maximum>15</messages-maximum>
    </client-params>
    <transaction-params>
        <xa-connection-factory-enabled>true</xa-connection-factory-enabled>
    </transaction-params>
    <security-params>
        <attach-jmsx-user-id>false</attach-jmsx-user-id>
    </security-params>
</connection-factory>

So in order to resolve this issue, there were a few necessary changes. The first change dealt with moving the clientId configuration out of Spring (which required the SingleConnectionFactory) and moving the clientId configuration into the WebLogic Server ConnectionFactory. It also required the modification of how the ConnectionFactory was configured with clientId properties. The configuration needed to change to add the clientId, allow the clientId to be duplicated, and change the subscription sharing policy. The JMS resource configuration changed the the following.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<connection-factory name="SampleConnectionFactory">
    <default-targeting-enabled>true</default-targeting-enabled>
    <jndi-name>com/foo/SampleConnectionFactory</jndi-name>
    <client-params>
                <client-id>sampleClientId</client-id>
            <client-id-policy>Unrestricted</client-id-policy>
            <subscription-sharing-policy>Sharable</subscription-sharing-policy>
                <messages-maximum>15</messages-maximum>
    </client-params>
    <transaction-params>
        <xa-connection-factory-enabled>true</xa-connection-factory-enabled>
    </transaction-params>
    <security-params>
        <attach-jmsx-user-id>false</attach-jmsx-user-id>
    </security-params>
</connection-factory>

Also changed the forwarding-policy for Topics from Replicated to Partitioned.

There was one last item that needed to be configured because of the use of UDTs in WebLogic Server as opposed to default Topics. UDTs do not inherently support durable subscribes unless the WAR subscribes to an individual member of the cluster. This requires the WAR to subscribe to a specific jmsServer instance. The problem is when there is a multiple machine cluster (jmsServer1, jmsServer2, etc) and a WAR is deployed to the cluster, how do we specify the individual jmsServer instance in the Spring configuration. This approach would be solved by supplying a system level property in the domain scripts of WebLogic Server (there is a start script per Managed Server) and so each Managed Server script would export a system property of the name of the jmsServer.

1
2
3
4
5
JMS_SERVER_NAME=jmsServer1
export JMS_SERVER_NAME

EXTRA_JAVA_PROPERTIES="$ $ $ $ -DjmsServer.Name=$ -Dweblogic.debug.DebugDeploy -Dweblogic.debug.MasterDeployer -Dweblogic.debug.ApplicationContainer -Dcom.sun.management.jmxremote.authenticate=false"
export EXTRA_JAVA_PROPERTIES

Then, the Spring configuration for the DefaultMessageListenerContainer could simply inject that system property into the JMS destinationName property, as such:

1
2
3
4
5
6
7
8
9
10
<bean id="sampleSubscribeServiceListenerContainer"  
    class="org.springframework.jms.listener.DefaultMessageListenerContainer"
    p:connectionFactory-ref="sampleMessagingConnectionFactory"
    p:destinationName="${jmsServer.Name:jmsServer1}@com/foo/dest/SampleTopic"
    p:destinationResolver-ref="sampleDestinationResolver"
    p:messageListener-ref="sampleSubscribeServiceMessageListener"
    p:pubSubDomain="true"
    p:subscriptionDurable="true"
    p:durableSubscriptionName="sampleSubscribeService"
    p:transactionManager-ref="GlobalDataTransactionManager"/>

This resolved the duplicate clientId issues, allowed the WARs to work in a cluster against UDTs, and also correctly allowed the UDTs to load balance incoming messages to each instance in the cluster.

Share and Enjoy

Spring and WebLogic Durable Subscriber

Based on a previous post, it was evident that continuing with the use of DefaultMessageListenerContainer with Topics, that messages can be lost. In order to resolve this issue, it is necessary to utilize Spring’s configuration (along with WebLogic requirements) for configuring a durable subscriber. This is needed in order to guarantee that when the DefaultMessageListenerContainer disconnects based on the receiveTimeout, that messages are not lost. Durable subscriber will also guarantee that as multiple subscribers to the Topic are connected/disconnected, that they will still be able process messages received while they were disconnected.

To have a good understanding of durable and just general JMS concepts, a good older article to read is Guaranteed Messaging with JMS.

And before I get too much in the details, the definitions defined in this snippet (Persistent vs Durable JMS messages), helps define the difference between durable and persistent – just to make sure I don’t get the two confused:

A ” durable message ” is a message where the JMS server will hold on to a message if the subscriber is temporarily unavaliable. So the durability is defined by the relationship between a “Topc Subscriber” and the “JMS Server”. Durability is applicable only to publish/Subscribe paradigm. For this to happen subscribers need to register themselves with a unique ” client id “.

A ” persistent message ” is a message that defines the relationship between a “Message Producer” and the “JMS Server”. This can be established for both point-to-point and publish/subscribe. This has to do with the guaranteed once only delivery of the message by persisting the message after it has been recieved from the message producer.

The ActiveMQ documentation gives us a little more information as well(How do durable queues and topics work):

Durable queues keep messages around persistently for any suitable consumer to consume them. Durable queues do not need to concern themselves with which consumer is going to consume the messages at some point in the future. There is just one copy of a message that any consumer in the future can consume.

Durable topics however are different as they must logically persist an instance of each suitable message for every durable consumer – since each durable consumer gets their own copy of the message.

So I need to take the following configuration that I had for a subscribe service against a Topic and make it durable.

1
2
3
4
5
6
7
8
<bean id="sampleSubscribeServiceListenerContainer"  
    class="org.springframework.jms.listener.DefaultMessageListenerContainer"
    p:connectionFactory-ref="sampleMessagingConnectionFactory"
    p:destinationName="com/foo/dest/SampleTopic"
    p:destinationResolver-ref="sampleDestinationResolver"
    p:messageListener-ref="sampleSubscribeServiceMessageListener"
    p:pubSubDomain="true"
    p:transactionManager-ref="GlobalDataTransactionManager"/>

The interesting part is that quite a bit of time can be taken on Google trying to find information on how to do this and the Spring documentation does not really seem to describe what is necessary for durable subscription.

Here are a few resources with some details as to creating durable subscriber services via Spring:

Persistent JMS Topics using ActiveMQ and Spring

So based on this article and a few more resources, it is apparent that I need to add some additional pieces of configuration:

1
2
3
4
5
6
7
8
9
10
11
<bean id="sampleSubscribeServiceListenerContainer"  
    class="org.springframework.jms.listener.DefaultMessageListenerContainer"
    p:connectionFactory-ref="sampleMessagingConnectionFactory"
    p:destinationName="com/foo/dest/SampleTopic"
    p:destinationResolver-ref="sampleDestinationResolver"
    p:messageListener-ref="sampleSubscribeServiceMessageListener"
    p:pubSubDomain="true"
    p:subscriptionDurable="true"
    p:durableSubscriptionName="sampleSubscribeService"
    p:clientId="sampleSubscribeService"
    p:transactionManager-ref="GlobalDataTransactionManager"/>

So I add three pieces of configuration (subscriptionDurable, durableSubscriptionName, and clientId), and then I deploy this to my server. Interestingly enough, the deployment works but the application fails on startup with the following error:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
weblogic.jms.common.IllegalStateException: [JMSClientExceptions:055032]An attempt was made to create a named consumer (sampleSubscribeService) on a connection with no clientID
    at weblogic.jms.client.JMSSession.throwWhenInvalidSubscriberName(JMSSession.java:2770) ~[weblogic.jar:10.3.5.0]
    at weblogic.jms.client.JMSSession.setupConsumer(JMSSession.java:2723) ~[weblogic.jar:10.3.5.0]
    at weblogic.jms.client.JMSSession.createConsumer(JMSSession.java:2691) ~[weblogic.jar:10.3.5.0]
    at weblogic.jms.client.JMSSession.createDurableSubscriber(JMSSession.java:2487) ~[weblogic.jar:10.3.5.0]
    at weblogic.jms.client.WLSessionImpl.createDurableSubscriber(WLSessionImpl.java:1233) ~[weblogic.jar:10.3.5.0]
    at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.createConsumer(AbstractPollingMessageListenerContainer.java:493) ~[spring-full.jar:3.0.5]
    at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.createListenerConsumer(AbstractPollingMessageListenerContainer.java:223) ~[spring-full.jar:3.0.5]
    at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.doReceiveAndExecute(AbstractPollingMessageListenerContainer.java:307) ~[spring-full.jar:3.0.5]
    at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:243) ~[spring-full.jar:3.0.5]
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1059) [spring-full.jar:3.0.5]
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1050) [spring-full.jar:3.0.5]
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:947) [spring-full.jar:3.0.5]
    at java.lang.Thread.run(Thread.java:662) [na:1.6.0_24]

So even though I defined a clientId within the configuration as is available through the Javadoc on DefaultMessageListenerContainer, I get hit with a clientID issue. If we go back to the ActiveMQ discussion we mentioned earlier, it gives some more information with regards to the importance of a clientID:

So for durable topic subscription, the JMS provider needs to be able to identify S when it shuts down and later on in the future reconnects, so it can know what messages to send to it while it was not running. JMS specification dictates that the identification of S is done by a combination of the clientID and the durable subscriber name. This is so that the JMS connection S uses can have many different durable subscriptions on different topics or on the same topic with different selectors – yet the JMS provider can know which message for which subscription to keep around for it.

So setting the clientID on a JMS connection is vital (along with using a sensible durable consumer name) for durable topic subscription.

So based upon this information, it appears that I need to have a clientId registered on the ConnectionFactory and not technically on the DefaultMessageListenerContainer (even though the API seems to allow it, it is not working based on the configuration I provided). So after quite a bit of searching, I found an article that I can’t seem to locate…but it points me towards SingleConnectionFactory which would allow me to wrap the ConnectionFactory that I am using and supply a clientId on that ConnectionFactory. Then I just utilize this new bean id (for the SingleConnectionFactory) in the configuration for my DefaultMessageListenerContainer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<bean id="sampleDurableTopicConnectionFactory"
    class="org.springframework.jms.connection.SingleConnectionFactory"
    p:targetConnectionFactory-ref="sampleMessagingConnectionFactory"
    p:clientId="sampleSubscribeService"/>

<bean id="sampleSubscribeServiceListenerContainer"  
    class="org.springframework.jms.listener.DefaultMessageListenerContainer"
    p:connectionFactory-ref="sampleDurableTopicConnectionFactory"
    p:destinationName="com/foo/dest/SampleTopic"
    p:destinationResolver-ref="sampleDestinationResolver"
    p:messageListener-ref="sampleSubscribeServiceMessageListener"
    p:pubSubDomain="true"
    p:subscriptionDurable="true"
    p:durableSubscriptionName="sampleSubscribeService"
    p:transactionManager-ref="GlobalDataTransactionManager"/>

So I passed in our JMS ConnectionFactory to the configuration of the SingleConnectionFactory, and moved the clientId configuration to it as well. Then I pass in the sampleDurableTopicConnectionFactory as the connectionFactory within the configuration for my DefaultMessageListenerContainer. I deploy this new web application and to my surprise, it still doesn’t work. I get the following error (which is the same error I get if I connect to a Uniform Distributed Topic (WLS) with HermesJMS:

1
2
3
4
5
6
7
8
weblogic.jms.common.JMSException: [JMSClientExceptions:055030]This topic does not support durable subscriptions
    at weblogic.jms.client.JMSSession.createDurableSubscriber(JMSSession.java:2482)
    at weblogic.jms.client.WLSessionImpl.createDurableSubscriber(WLSessionImpl.java:1233)
    at hermes.impl.TopicBrowser.getEnumeration(TopicBrowser.java:220)
    at hermes.browser.tasks.BrowseDestinationTask.invoke(BrowseDestinationTask.java:146)
    at hermes.browser.tasks.TaskSupport.run(TaskSupport.java:175)
    at hermes.browser.tasks.ThreadPool.run(ThreadPool.java:170)
    at java.lang.Thread.run(Unknown Source)

Now I mentioned this in a previous blog post, but with WebLogic Uniform Distributed Topics, you have to do special configuration within a Subscribe Service in order to use Durable Subscriber since WebLogic does not allow a Durable Subscriber against a Uniform Distributed Topic (unless you point to one of the specific members…by including a single JMS Server in the JNDI url…like jmsServer1@/com/foo/SampleBasicTopic). The reason being is explained in the WebLogic Server Documentation:

Note: Durable subscribers (DurableTopicSubscriber) cannot be created for distributed topics. However, you can still create a durable subscription on distributed topic member and the other topic members will forward the messages to the topic member that has the durable subscription.

So in order to make this work, I simply need to modify the destinationName to include the JMS Server name:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<bean id="sampleDurableTopicConnectionFactory"
    class="org.springframework.jms.connection.SingleConnectionFactory"
    p:targetConnectionFactory-ref="sampleMessagingConnectionFactory"
    p:clientId="sampleSubscribeService"/>

<bean id="sampleSubscribeServiceListenerContainer"  
    class="org.springframework.jms.listener.DefaultMessageListenerContainer"
    p:connectionFactory-ref="sampleDurableTopicConnectionFactory"
    p:destinationName="jmsServer1@com/foo/dest/SampleTopic"
    p:destinationResolver-ref="sampleDestinationResolver"
    p:messageListener-ref="sampleSubscribeServiceMessageListener"
    p:pubSubDomain="true"
    p:subscriptionDurable="true"
    p:durableSubscriptionName="sampleSubscribeService"
    p:transactionManager-ref="GlobalDataTransactionManager"/>

I rebuild my war and deploy, and luckily this time, it works. I now have a durable subscriber. The interesting part is that as I start clicking around WebLogic console and look at the configured ConnectionFactory, I notice a Client Id configuration.

With this newly discovered piece of information, I find some more information that clarifies the role of the Client Id in Developing Advanced Pub/Sub Applications.

A subscription key is used to uniquely identify a subscription. For non-durable subscriptions, the key is composed of the Client ID and Client ID Policy. For durable subscriptions, the key is composed of the Client ID, Client ID Policy, and Subscription Name.

So this clarifies that I should just be able to set the Client Id on the ConnectionFactory within WebLogic and remove the configuration of the SingleConnectionFactory from my Spring configuration. So I went ahead and modified my ConnectionFactory by adding into the Client Id the value “sampleSubscribeService”, and then removed the SingleConnectionFactory configuration from my Spring Context file, rebuild/redeployed my web application, and my subscribe service works with this configuration as well. I can even go into the WebLogic Server console, click on the my JMS Resource (the Topic), go to Monitoring, and I should see a “Durable Subscribers” tab with the subscription name and client id that I defined.

Share and Enjoy

Spring DefaultMessageListenerContainer and Loss of Topic Messages

As I start developing JMS based services, I need to determine how I will architecture consumers and what types of performance best practices should be considered. First I took the time to read through the Spring Documentation and Spring Recipes book, but then read through these next two posts:

Using Spring to Send JMS Messages
Using Spring to Receive JMS Messages

There are some excellent details in this article and after evaluation of the multiple implementation types for Spring based consumers, I decided to use DefaultMessageListenerContainer with the intregration of MessageListener. I also have needs for my consumers to have transactionality, in some cases I want the transaction to participate in a JTA and in other cases I want some more control over rollbacks and message handling by implementing JMS based transactions. Searching for some general understanding of JMS Performance Best Practices also brings to light the needs around certain configuration options. I took a look at articles like “JMS Performance like Best practices to improve performance in JMS“, which will talk about understanding when to and not to use persistent messages.

It seems like JMS has a plethora of configurations that really are specific to the unique case of each implementation of a JMS consumer, but for now, I will build DefaultMessageListenerContainers with most of the default capabilities regardless if I am listening to Queues or Topics.

Below is an example of a default DefaultMessageListenerContainer for a Queue

1
2
3
4
5
6
7
8
9
10
<bean id="sampleSubscribeServiceListenerContainer"  
    class="org.springframework.jms.listener.DefaultMessageListenerContainer"
    p:connectionFactory-ref="sampleMessagingConnectionFactory"
    p:destinationName="com/foo/dest/SampleQueue"
    p:destinationResolver-ref="sampleDestinationResolver"
    p:messageListener-ref="sampleSubscribeServiceMessageListener"
    p:transactionManager-ref="GlobalDataTransactionManager"/>
or
    <!-- Use instead of transactionManager-ref for JMS Transactions-->
    p:sessionTransacted="true"/>

And here is an example of a DefaultMessageListenerContainer for a Topic

1
2
3
4
5
6
7
8
9
10
11
<bean id="sampleSubscribeServiceListenerContainer"  
    class="org.springframework.jms.listener.DefaultMessageListenerContainer"
    p:connectionFactory-ref="sampleMessagingConnectionFactory"
    p:destinationName="com/foo/dest/SampleTopic"
    p:destinationResolver-ref="sampleDestinationResolver"
    p:messageListener-ref="sampleSubscribeServiceMessageListener"
    p:pubSubDomain="true"
    p:transactionManager-ref="GlobalDataTransactionManager"/>
or
    <!-- Use instead of transactionManager-ref for JMS Transactions-->
    p:sessionTransacted="true"/>

Everything was working perfectly until the amount of messages that need to be consumed increased and seemed to overwhelm the consumers of Topics, because messages were starting to get lost. For instance, I could either create a JMS Client or use HermesJMS to send a message to a Topic and then watch the server logs (as well as attach to the Topic via HermesJMS). I could essentially send 10 messages through and HermesJMS would receive all 10 and the consumer on the server would only show 9. The servers/apps were never restarted, so initially the concern wasn’t with regards to whether the messages were persistent or not. But once I searched for “defaultmessagelistenercontainer lose message topic”, I started receiving more details about the architecture of the different types of Spring containers.

Many discussions or forum postings simply told the developer that the consumer wasn’t working because it was not durable, but did that mean I couldn’t have a Topic consumer that didn’t have to be durable?

http://stackoverflow.com/questions/10851064/losing-messages-from-a-topic-with-defaultmessagelistenercontainer

Then I found a JIRA entry related to exactly the issue that was occurring:

https://jira.springsource.org/browse/SPR-7883

This gave a little more detail, but the true clarity was with the following forum post:

http://forum.springsource.org/showthread.php?33037-What-s-the-best-practice-for-using-JMS-in-spring

While DefaultMessageListenerContainer does use a pull approach behind the scenes, it is still considered a recommendable approach in many environments. After all, any kind of listening approach always boils down to some kind of receive loop on the underlying socket connection; it’s only really about the level at which it happens.

DefaultMessageListenerContainer’s advantage is the level of control that it gives, in particular with respect to thread management and transaction demarcation. DMLC is the only listener container that does not impose the thread management onto the JMS provider, that is, does not use/block JMS provider threads. It is also able to gracefully recover from JMS provider failure, such as connection loss. Furthermore, as noted, it is the only variant that supports external transaction managers, in particular for XA transactions.

DefaultMessageListenerContainer is also the only listener container variant that is compatible with both managed JMS and non-managed JMS, i.e. JMS in a J2EE environment as well as standalone JMS usage, since it sticks with standard JMS API that is compatible with all of J2EE’s JMS restrictions. SimpleMessageListenerContainer on the other hand uses “Session.setMessageListener”, not supported in a J2EE environment.

The only issue in a J2EE environment is thread management: On WebLogic and WebSphere, we recommend to specify a CommonJ WorkManagerTaskExecutor, which makes DMLC delegate to a server-managed thread pool, resulting in fully integrated thread management. Alternatively, simply stick with the default SimpleAsyncTaskExecutor, which works fine as well in many cases. (It will work just as well as Quartz does…)

Regarding transaction management: If you specify a “transactionManager” on DefaultMessageListenerContainer, it will wrap its receive loop in a transaction. This is typically used with JtaTransactionManager, where no transactional resources are actually bound: It’s only really marking the thread (and the listener’s JMS Session) as “XA-active” and waiting for something to happen. This should be pretty efficient with any decent JTA provider.

As a consequence, DefaultMessageListenerContainer is the variant to use in a J2EE environment, and also a primary choice for native JMS usage. It is often used with native JMS providers such as Tibco, talking to an external broker process (even when running in a J2EE server), typically without XA transactions. Alternatively, consider using SimpleMessageListenerContainer, but only for native JMS usage without XA, and only if your JMS provider gracefully handles thread management and connection recovery.

FWIW, there is a further approach for JMS listening, for native JMS usage only but with the provider pushing incoming messages – and with full XA support: the JCA 1.5 message endpoint mechanism. It requires a JMS 1.1 provider that ships a JCA 1.5 connector (e.g. ActiveMQ), running in a local JCA container within the application. This is the approach that the Jencks project (http://jencks.org) takes, as a third-party add-on to Spring: worth considering if XA transactions are needed, as alternative to DMLC.

-Juergen Hoeller

Bingo, now it all makes sense. So the core issue is not really the durable/non-durable, it is around the type of consumer listener (one that polls or one that pushes). And since I was using the default settings and DefaultMessageListenerContainer, the thread for the DefaultMessageListenerContainer would wake up and stay alive for a second and if it did not find a message, it would close and be recycled. So this happens every single second because of the default of the “receiveTimeout”. When this dropping of the connection happens, there is a potential for a message to be lost because at that split second or so, there is no consumer subscribed to the Topic (which is why the durable fixes this issue).

But this now exposes an interesting concept. In the case that an XA Transaction is being used, the XA AOP Pointcuts have been designed without a timeout to prevent run away threads. One of the suggestions in the discussion is to put a negative value in the “receiveTimeout”, but the problem is that this would potentially result in XA Timeouts anytime the thread didn’t find a message within the allotted XA timeout configuration. So to alleviate this problem, I will set the “receiveTimeout” to the XA AOP timeout (which for these consumers is 180 seconds). This will not fix the loss of messages, because at the end of the 180 seconds when the connection is recycled, I could still lose messages. But this will save the resources of reconnecting every single second waiting for a message. The only thing I need to be concerned is how do I configure the “receiveTimeout” for a Topic versus a Queue. Topics should essentially only have one concurrent consumer (otherwise they could potentially receive a message multiple times), but a Queue can have multiple concurrent consumers (to assist with performance). I just now need to be aware that if I set the “receiveTimeout” to 180 seconds and the concurrentConsumers=”5″ and happen to have 5 different DefaultMessageListenerContainers, then I potentially have a minimum of 25 threads waiting around for 180 seconds. So I just need to make sure I understand how many potential threads could be opened across my domain and any potential resource issues.

So in order to complete the configuration of a Topic consumer so that it does not lose messages, I will need to add configuration to define what is necessary to make it durable. This will be covered in another blog post.

Share and Enjoy