RabbitMQ JMS Client Performance Tuning

151 views
Skip to first unread message

Jerry Yu

unread,
Sep 9, 2019, 3:09:45 PM9/9/19
to rabbitmq-users
Hello,

Wondering if anybody here can kindly advice on how to tune the performance of RabbitMQ JMS Client (rabbitmq-jms-client). We have an application using ActiveMQ currently, with Spring Integration (<int-jms:outbound-channel-adapter> and DefaultMessageListenerContainer), but would like to switch to RabbitMQ. We did some performance tests, and found out that the producer throughput of RabbitMQ is much lower than that of ActiveMQ. This is the connection factory code:

private ConnectionFactory connectionFactory() throws Exception {

int channelsQos = 100;

RMQConnectionFactory factory = new RMQConnectionFactory();

factory.setHost(host);

factory.setVirtualHost(vhost);

factory.setPort(port);

factory.setUsername(user);

factory.setPassword(pass);

factory.setChannelsQos(channelsQos);

return new SingleConnectionFactory(factory);

}


Any suggests/comment would be appreciated! 

Jerry

Wesley Peng

unread,
Sep 9, 2019, 11:52:27 PM9/9/19
to rabbitm...@googlegroups.com
Hi

on 2019/9/10 3:09, Jerry Yu wrote:
> Wondering if anybody here can kindly advice on how to tune the
> performance of RabbitMQ JMS Client (rabbitmq-jms-client). We have an
> application using ActiveMQ currently, with Spring Integration
> (<int-jms:outbound-channel-adapter> and
> DefaultMessageListenerContainer), but would like to switch to RabbitMQ.
> We did some performance tests, and found out that the producer
> throughput of RabbitMQ is much lower than that of ActiveMQ. This is the
> connection factory code:

You can setup queue content to memory only, and don't use an
username/password for authentication. Also rabbitmq cluster would have
the ability to improve the throughput.

regards.

Arnaud Cogoluègnes

unread,
Sep 10, 2019, 5:40:35 AM9/10/19
to rabbitm...@googlegroups.com
More information about your benchmark would be helpful. Ideally a
project with broker settings and steps to run would help us to help
you.

On another note, I'd advice to get rid of JMS altogether in this case
and use Spring Integration AMQP adapters. One of Spring Integration's
goals is to isolate application code from infrastructure like JMS or
AMQP, so switching from JMS to AMQP should have minimal impact on your
application code and would allow for fairer comparison between those
technologies.
> --
> You received this message because you are subscribed to the Google Groups "rabbitmq-users" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to rabbitmq-user...@googlegroups.com.
> To view this discussion on the web, visit https://groups.google.com/d/msgid/rabbitmq-users/df6a858e-a903-4397-a9a4-3c1e67a8d8a1%40googlegroups.com.

Jerry Yu

unread,
Sep 10, 2019, 9:46:58 AM9/10/19
to rabbitmq-users
Thanks Wesley! Authentication is required for the application. The ActiveMQ server that we compared for our tests is also having the authentication. 
The RMQ is also already on a cluster.

Jerry Yu

unread,
Sep 10, 2019, 4:24:34 PM9/10/19
to rabbitm...@googlegroups.com
Thanks Arnaud! I may try the Spring Integration's AMQP Adapter later. But right now, it's not the goal for me to replace JMS in the application. Followings are some details I found which may have caused the performance issue.

For example, when I sent 2 messages all together to a queue via Spring Integration <int-jms:outbound-channel-adapter>. With logging enabled, I can see that followings happened for EACH message to be sent.

Create Producer, 
Declare queue destination and exchange, 
exchange and queue binding, 
close producer, 
close session.

I am wondering how to set spring integration to share the producer without closing the producer and session when sending each messages. Would like the messages to be send in batch.

Log snippet for message 1:
[com.rabbitmq.jms.client.RMQSession] [#631] create producer for destination 'RMQDestination{destinationName
[com.rabbitmq.jms.client.RMQSession] [#829] declare RabbitMQ queue for destination 'RMQDestination{destinationName
[com.rabbitmq.jms.client.RMQSession] [#857] declare RabbitMQ exchange for queue destinations 'RMQDestination{destinationName
[com.rabbitmq.jms.client.RMQSession] [#876] declare RabbitMQ queue name, durable(true), exclusive(false), auto-delete(false), properties(null)
[com.rabbitmq.jms.client.RMQSession] [#893] bind queue name to exchange(jms.durable.queues)
[com.rabbitmq.jms.client.RMQMessageProducer] [#263] send/publish message(com.rabbitmq.jms.client.message.RMQObjectMessage@1f) to destination(RMQDestination deliveryMode(2), priority(4), timeToLive(0)
[com.rabbitmq.jms.client.RMQMessageProducer] [#212] close producer for destination 'RMQDestination{destinationName
[com.rabbitmq.jms.client.RMQConnection] [#480] internal:sessionClose(com.rabbitmq.jms.client.RMQSession)
 [com.rabbitmq.jms.client.RMQSession] [#497] close session com.rabbitmq.jms.client.RMQSession

Got same log snippet for message 2:
Log snippet for message 1
[com.rabbitmq.jms.client.RMQSession] [#631] create producer for destination 'RMQDestination{destinationName
[com.rabbitmq.jms.client.RMQSession] [#829] declare RabbitMQ queue for destination 'RMQDestination{destinationName
[com.rabbitmq.jms.client.RMQSession] [#857] declare RabbitMQ exchange for queue destinations 'RMQDestination{destinationName
[com.rabbitmq.jms.client.RMQSession] [#876] declare RabbitMQ queue name, durable(true), exclusive(false), auto-delete(false), properties(null)
[com.rabbitmq.jms.client.RMQSession] [#893] bind queue name to exchange(jms.durable.queues)
[com.rabbitmq.jms.client.RMQMessageProducer] [#263] send/publish message(com.rabbitmq.jms.client.message.RMQObjectMessage@1f) to destination(RMQDestination deliveryMode(2), priority(4), timeToLive(0)
[com.rabbitmq.jms.client.RMQMessageProducer] [#212] close producer for destination 'RMQDestination{destinationName
[com.rabbitmq.jms.client.RMQConnection] [#480] internal:sessionClose(com.rabbitmq.jms.client.RMQSession)
 [com.rabbitmq.jms.client.RMQSession] [#497] close session com.rabbitmq.jms.client.RMQSession


Thanks,

Jerry


Arnaud Cogoluègnes

unread,
Sep 11, 2019, 4:46:26 AM9/11/19
to rabbitm...@googlegroups.com
I set up a small Spring Integration project.

This is the first setup:

<bean id="jmsConnectionFactory"
class="com.rabbitmq.jms.admin.RMQConnectionFactory" />

<bean id="connectionFactory"
class="org.springframework.jms.connection.SingleConnectionFactory">
<constructor-arg ref="jmsConnectionFactory" />
</bean>

<int:channel id="inJmsChannel" />

<int-jms:outbound-channel-adapter id="jmsOut"
destination-name="spring-integration" channel="inJmsChannel"
connection-factory="connectionFactory" />

If I publish a couple of messages in the channel, I see the following
on Wireshark:

"58","2.504461112","127.0.0.1","127.0.0.1","AMQP","577","Connection.Start "
"60","2.547604505","127.0.0.1","127.0.0.1","AMQP","457","Connection.Start-Ok "
"61","2.548212960","127.0.0.1","127.0.0.1","AMQP","86","Connection.Tune "
"63","2.556176215","127.0.0.1","127.0.0.1","AMQP","86","Connection.Tune-Ok "
"64","2.557525791","127.0.0.1","127.0.0.1","AMQP","82","Connection.Open
vhost=/ "
"66","2.557678405","127.0.0.1","127.0.0.1","AMQP","79","Connection.Open-Ok "
"67","2.580389321","127.0.0.1","127.0.0.1","AMQP","79","Channel.Open "
"68","2.581045345","127.0.0.1","127.0.0.1","AMQP","82","Channel.Open-Ok "
"69","2.584595436","127.0.0.1","127.0.0.1","AMQP","111","Exchange.Declare
x=jms.durable.queues "
"70","2.584828459","127.0.0.1","127.0.0.1","AMQP","78","Exchange.Declare-Ok "
"71","2.586109862","127.0.0.1","127.0.0.1","AMQP","104","Queue.Declare
q=spring-integration "
"72","2.586435554","127.0.0.1","127.0.0.1","AMQP","105","Queue.Declare-Ok
q=spring-integration "
"73","2.587478086","127.0.0.1","127.0.0.1","AMQP","142","Queue.Bind
q=spring-integration x=jms.durable.queues bk=spring-integration "
"74","2.587882599","127.0.0.1","127.0.0.1","AMQP","78","Queue.Bind-Ok "
"75","2.599469545","127.0.0.1","127.0.0.1","AMQP","939","Basic.Publish
x=jms.durable.queues rk=spring-integration Content-Header
type=application/octet-stream Content-Body "
"76","2.602071846","127.0.0.1","127.0.0.1","AMQP","87","Channel.Close reply=OK "
"78","2.602833698","127.0.0.1","127.0.0.1","AMQP","78","Channel.Close-Ok "
"79","2.604917113","127.0.0.1","127.0.0.1","AMQP","79","Channel.Open "
"80","2.605852141","127.0.0.1","127.0.0.1","AMQP","82","Channel.Open-Ok "
"81","2.606439638","127.0.0.1","127.0.0.1","AMQP","111","Exchange.Declare
x=jms.durable.queues "
"82","2.606586132","127.0.0.1","127.0.0.1","AMQP","78","Exchange.Declare-Ok "
"83","2.606830753","127.0.0.1","127.0.0.1","AMQP","104","Queue.Declare
q=spring-integration "
"84","2.607044297","127.0.0.1","127.0.0.1","AMQP","105","Queue.Declare-Ok
q=spring-integration "
"85","2.607340808","127.0.0.1","127.0.0.1","AMQP","142","Queue.Bind
q=spring-integration x=jms.durable.queues bk=spring-integration "
"86","2.607827156","127.0.0.1","127.0.0.1","AMQP","78","Queue.Bind-Ok "
"87","2.608750773","127.0.0.1","127.0.0.1","AMQP","939","Basic.Publish
x=jms.durable.queues rk=spring-integration Content-Header
type=application/octet-stream Content-Body "
"88","2.609249846","127.0.0.1","127.0.0.1","AMQP","87","Channel.Close reply=OK "
"90","2.609406799","127.0.0.1","127.0.0.1","AMQP","78","Channel.Close-Ok "

This is far from optimal indeed.

You can declare the destination as a Spring bean and reference it in
the adapter, this way Spring should not create a dynamic destination
and this avoids the double declaration:

<bean id="jmsDestination" class="com.rabbitmq.jms.admin.RMQDestination" >
<constructor-arg value="spring-integration" />
<constructor-arg value="false" />
<constructor-arg value="false" />
</bean>

<int-jms:outbound-channel-adapter id="jmsOut"
destination="jmsDestination" channel="inJmsChannel"
connection-factory="connectionFactory" />

Wireshark capture:

"111","17.295073485","127.0.0.1","127.0.0.1","AMQP","577","Connection.Start "
"113","17.337805756","127.0.0.1","127.0.0.1","AMQP","457","Connection.Start-Ok "
"114","17.338038282","127.0.0.1","127.0.0.1","AMQP","86","Connection.Tune "
"116","17.343033765","127.0.0.1","127.0.0.1","AMQP","86","Connection.Tune-Ok "
"117","17.343530877","127.0.0.1","127.0.0.1","AMQP","82","Connection.Open
vhost=/ "
"119","17.343660142","127.0.0.1","127.0.0.1","AMQP","79","Connection.Open-Ok "
"120","17.362400134","127.0.0.1","127.0.0.1","AMQP","79","Channel.Open "
"121","17.363057672","127.0.0.1","127.0.0.1","AMQP","82","Channel.Open-Ok "
"122","17.365572542","127.0.0.1","127.0.0.1","AMQP","109","Exchange.Declare
x=jms.durable.topic "
"123","17.365753947","127.0.0.1","127.0.0.1","AMQP","78","Exchange.Declare-Ok "
"124","17.376954955","127.0.0.1","127.0.0.1","AMQP","937","Basic.Publish
x=jms.durable.topic rk=spring-integration Content-Header
type=application/octet-stream Content-Body "
"125","17.379511567","127.0.0.1","127.0.0.1","AMQP","87","Channel.Close
reply=OK "
"127","17.379703825","127.0.0.1","127.0.0.1","AMQP","78","Channel.Close-Ok "
"128","17.382036014","127.0.0.1","127.0.0.1","AMQP","79","Channel.Open "
"129","17.382816187","127.0.0.1","127.0.0.1","AMQP","82","Channel.Open-Ok "
"130","17.383672229","127.0.0.1","127.0.0.1","AMQP","937","Basic.Publish
x=jms.durable.topic rk=spring-integration Content-Header
type=application/octet-stream Content-Body "
"131","17.384011166","127.0.0.1","127.0.0.1","AMQP","87","Channel.Close
reply=OK "
"133","17.384169121","127.0.0.1","127.0.0.1","AMQP","78","Channel.Close-Ok "

We can save on resources by using a CachingConnectionFactory:

<bean id="connectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory">
<constructor-arg ref="jmsConnectionFactory" />
</bean>

Wireshark capture:

"65","6.118300306","127.0.0.1","127.0.0.1","AMQP","577","Connection.Start "
"67","6.153000478","127.0.0.1","127.0.0.1","AMQP","457","Connection.Start-Ok "
"68","6.153268213","127.0.0.1","127.0.0.1","AMQP","86","Connection.Tune "
"69","6.165255448","127.0.0.1","127.0.0.1","AMQP","86","Connection.Tune-Ok "
"70","6.166737796","127.0.0.1","127.0.0.1","AMQP","82","Connection.Open
vhost=/ "
"72","6.166893772","127.0.0.1","127.0.0.1","AMQP","79","Connection.Open-Ok "
"73","6.186763082","127.0.0.1","127.0.0.1","AMQP","79","Channel.Open "
"74","6.187441629","127.0.0.1","127.0.0.1","AMQP","82","Channel.Open-Ok "
"75","6.199247326","127.0.0.1","127.0.0.1","AMQP","109","Exchange.Declare
x=jms.durable.topic "
"76","6.199746050","127.0.0.1","127.0.0.1","AMQP","78","Exchange.Declare-Ok "
"77","6.214998961","127.0.0.1","127.0.0.1","AMQP","937","Basic.Publish
x=jms.durable.topic rk=spring-integration Content-Header
type=application/octet-stream Content-Body "
"78","6.218911356","127.0.0.1","127.0.0.1","AMQP","937","Basic.Publish
x=jms.durable.topic rk=spring-integration Content-Header
type=application/octet-stream Content-Body "

This is more realistic with those settings. I noticed the logs can be
misleading sometimes, so you're better off relying on a network
capture to see what really goes on the wire.

In the future, please provide more information like source code,
Spring configuration, versions you're using.

Thanks.
> To view this discussion on the web, visit https://groups.google.com/d/msgid/rabbitmq-users/CAP2AAQMveUvVQyh9-rWWABQ%3DnvErY9yf40bzmxyQhLwB9V3RKw%40mail.gmail.com.

Jerry Yu

unread,
Sep 11, 2019, 8:26:56 AM9/11/19
to rabbitm...@googlegroups.com
Hi Arnaud,

It looks awesome. It's exactly what I was looking for.  I will try it out. Might be worth adding some tips to RabbitMQ-jms-client web page or sample code to GitHub repo to show users on how to use the library efficiently in Spring.

Thanks a lot, really appreciate it.

Jerry

Reply all
Reply to author
Forward
0 new messages