-
Which is also where the difference is... When using JmsTemplate the producer is recreated each time (and if you are unlucky also the session) which takes time. Your plain solution creates a session/producer when constructed and reuse it always. So there is a difference there.
Also you are using a single connection for both receiving and sending in the spring sample might be of influence.
To get more information on what happens inside spring you might want to enable trace logging (this will impact performace!) for the org.springframework.jms package. That way you can see what happens.
Another note the CachingConnectionFactory is already a single connectionFactory so you don't need to point it to another singleconnectionfactory.
Your MessageListener isn't caching anything (but the connection) using a JmsTransactionManager will lead to caching of the consumer instead of recreation of consumers (or setting the cacheLevel property to 3 for caching/reusing the consumer) or use the cachingCOnnectionFactory instead of the singleConnectionFactory.
-
thanks for the reply.
I have a few questions:
1. So if I understand this correctly, since i am using cachingConnectionFactory for jmsTemplate, it reuses the session/producer.
doesn't it mean that this should increase performance since the session and producer creation each time is costly?
2. As for reusing the connection for sending and receiving, do you mean that I should separate this and use different connections for consumer and producer? even if this is the consumer and producer which is on the same process (i have a consumer & producer per process, they don't communicate within the same process)
3. you say that the MessageListener does not cache the consumers. but i defined concurrentConsumers=20. doesn't it mean that 20 are kept and reused?
thanks
-
1. In theory it should reuse but there is a key generated to determine the reuse or not (hence my suggestion to turn on TRACE logging to see if this is actually happening)
2. In your plain sample you also have different connections hence to create a consistent sample you should do the same for the spring one (or change the plain one to use a single connection).
3. No... It means that there are 20 threads consuming messages it doesn't mean that it reuses MessageConsumers (in general after a poll they are closed!).
Edit: Another difference is that Spring uses default PERSISTENT to send messages you use NON_PERSISTENT, switching that made a huge difference (at least for the plain client) when sending messages.
-
Thanks for the great reply.
I tested what you suggested:
1. separating the connections between producer and consumer increased the performance greatly (300k on 2 directions~ 600k took 45 seconds instead of about 75 seconds)
2. updating to caching in the consumer instead of single & update the cacheLevel to 3- had no impact on performance - i tried the transaction manager but for some reason
my computer got stuck every time i ran it and i had to restart the machine - i am not sure how this relates, maybe many connections are created.
3. I updated the deliveryMode in jmsTemplate to be not persistence - but i didn't see any impact on performance which is weird.
This is the new spring xml file:
Code:
<!-- ********************** Consumer ********************************* -->
<bean id="clientListener" class="example.ClientReceive">
<property name="receiveQueue" ref="receiveQueue" />
<property name="sinkQueue" ref="sinkQueue" />
<property name="clientSender" ref="clientSender"/>
</bean>
<bean id="containerClient"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="concurrentConsumers" value="20"/><!-- if it is not 1 - it is difficult to see distribution to other services!!!change to 50,20 -->
<property name="maxConcurrentConsumers" value="50"/><!-- if it is not 1 - it is difficult to see distribution to other services!!!change to 50-->
<property name="idleTaskExecutionLimit" value="10"/>
<property name="connectionFactory" ref="cachingConnectionFactoryConsumer" />
<property name="destination" ref="receiveQueue" />
<property name="messageListener" ref="clientListener" />
<property name="cacheLevel" value="3"/> <!-- CACHE_CONSUMER: cache a shared JMS Connection, a JMS Session, and a JMS MessageConsumer for each listener thread. -->
<property name="sessionAcknowledgeMode" value="1"/>
</bean>
<bean id="receiveQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="queueA"/>
</bean>
<bean id="sinkQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="sinkQueue"/>
</bean>
<bean id="connectionFactoryConsumer"
class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="failover:(tcp://localhost:61616)?randomize=false" /> <!-- Single broker-->
</bean>
<bean id="singleConnectionFactoryConsumer" class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory"
ref="connectionFactoryConsumer" />
<property name="reconnectOnException" value="true"/>
</bean>
<bean id="cachingConnectionFactoryConsumer"
class="org.springframework.jms.connection.CachingConnectionFactory">
<!-- <constructor-arg ref="singleConnectionFactoryConsumer"/> -->
<constructor-arg ref="connectionFactoryConsumer"/>
<property name="sessionCacheSize" value="100" />
</bean>
<!--<bean id="jmsTransactionManagerConsumer" class="org.springframework.jms.connection.JmsTransactionManager">
<property name="connectionFactory" ref="connectionFactoryConsumer" />
</bean>-->
<!-- ********************** END-Consumer ***************************** -->
<!-- ********************** Producer ********************************* -->
<bean id="clientSender" class="example.ClientSend">
<property name="sendQueue" ref="sendQueue" />
<property name="jmsTemplate" ref="jmsTemplate"/>
</bean>
<bean id="sendQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="queueB"/>
</bean>
<!-- for send response on temp q-->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate" lazy-init="true">
<property name="connectionFactory" ref="cachingConnectionFactoryProducer"/>
<property name="deliveryMode" value="1"/>
</bean>
<bean id="connectionFactoryProducer"
class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="failover:(tcp://localhost:61616)?randomize=false" /> <!-- Single broker-->
</bean>
<bean id="cachingConnectionFactoryProducer"
class="org.springframework.jms.connection.CachingConnectionFactory">
<constructor-arg ref="connectionFactoryProducer"/>
<property name="sessionCacheSize" value="100" />
</bean>
<!-- ********************** END Producer ********************************* -->
-
Did more or less the same.
Sending 5K messages with a plain producer takes ~1 second less as compared with jmsTemplate. This is without connection sharing between the listener container and template. When I enable that jmsTemplate takes 3 times as long as a plain version but that is probably due to the multithreading and contention for the pool/caching mechanism.
When using the cachingConnectionFactory you don't need to additionally set the cache level, caching is now handled by the connectionfactory and its related classes so this doesn't impact performance (at least not noticably I suspect).
Also tried playing around with (NON)Persistent but that appears to be changing nothing for the JmsTemplate it has a great impact ont he plain client when setting it to Persistent.
Edit: Next to setting the delivery mode you also need to enable explicitQosEnabled on the JmsTemplate (forgot about that :) ).
-
After adding the explicitQosEnabled - i see an improvement. so now it takes about 33 seconds instead of 75 seconds.
This is great news.
thanks a lot.
-
If you change the plain client to use the same send method as JmsTemplate does send(message, deliverymode, priority, ttl) you will see that the performance difference is even smaller.
Jmstemplate has a (small) overhead as it does some delegation and checking which you don't have to do when doing all the stuff manually. This is the price to pay for using a more abstract solution. IMHO it is worth it but that is for each to decide.