Book Image

Instant Apache ActiveMQ Messaging Application Development How-to

By : Timothy A. Bish
Book Image

Instant Apache ActiveMQ Messaging Application Development How-to

By: Timothy A. Bish

Overview of this book

Apache ActiveMQ is a powerful and popular open source messaging and Integration Patterns server. ActiveMQ is a fully JMS 1.1 compliant Message Broker and supports many advanced features beyond the JMS specification.Instant ActiveMQ Application Development How-to shows you how to get started with the ActiveMQ Message Broker. You will learn how to develop message-based applications using ActiveMQ and the JMS specification. In this book you will learn all the basic skills you need to start writing Java Messaging applications with a firm grounding in the more advanced features of ActiveMQ, giving you the tools to continue to master application development using ActiveMQ. Starting by applying the messaging features of the JMS specification to write basic messaging applications, you will develop a basic JMS application using topics and queues to broadcast events as well as perform Request and Response operations over the JMS. Once you have mastered the simple tasks you will move onto using the advanced features in ActiveMQ to supercharge your messaging applications. You will get to grips with ActiveMQ's scheduler to delay messages. You will also learn how to leverage ActiveMQ's fault-tolerant capabilities to create robust client applications.
Table of Contents (7 chapters)

Using ActiveMQ connection pools (Advanced)


In this recipe we will take a look at ActiveMQ's connection pooling library and see how using it in your applications can increase performance and reduce the load placed on your broker.

Getting ready

For this recipe, we will use the example application named connection-pools to demonstrate the use of the ActiveMQ pooling library. Before proceeding, you should ensure that there are no other broker instances running on your computer since our example uses the embedded broker trick we learned previously.

How to do it...

To run the sample for this recipe, open a terminal, change the path to the directory where connection-pools is located, and run it by typing mvn compile exec:java.

In the terminal where you started the example, you will see output like the following, indicating that the application is running:

Starting Pooled Connection example now...
Non-Pooled test took: 13 seconds
Pooled test took: 2 seconds
Finished running the Pooled Connection example.

How it works...

While the JMS API is connection oriented, meaning that our JMS applications are intended to open a connection and keep it open for long periods of time, not all applications fit this model. This is even though they use JMS as their messaging framework. ActiveMQ provides a solution to this problem by supplying a connection pooling library that allows your code more flexibility when it comes to working with JMS connections.

The ActiveMQ pooling library provides a solution for three common pooling scenarios when using the JMS API:

  • Pooling of Connection objects for applications that either need to share a single connection or a set of connections among code that would otherwise result in many connections being opened and closed over time.

  • Pooling of Session objects for a single connection for an application that would otherwise create and destroy large numbers of sessions over time.

  • Pooling of MessageProducer objects for applications that either need a large number of MessageProducer instances or that create and destroy many MessageProducer instances over time.

Before we can use the pooling library effectively, we need to understand how it works and what we need to configure to better suit our applications' needs. In the next figure, we see the basic structure of classes that make up the pooling library and how they interact with each other:

The first thing you should notice about the pooling library code is that it maintains the same structure as the standard ActiveMQ JMS client code. The code you write to use the pooling library is the standard JMS API code we've already learned to use; it's what's on the inside that counts. Let's examine each level of the library, and then we'll talk about how the example application for this recipe works.

The PooledConnectionFactory class

The PooledConnectionFactory class is our entry point into the pooling library; applications that use it generally create one PooledConnectionFactory instance and share it throughout the application code. PooledConnectionFactory exposes a number of configuration options to control how the Connection instances it creates behave. By default, PooledConnectionFactory creates a single Connection instance and returns that same instance every time the createConnection() methods are called.

Some applications, such as the example used in this recipe, can benefit from having more than one Connection instance in the pool, and it's easy enough to configure this using the factory's setMaxConnections() method. When the pool contains more than one connection, the connections are handed out in a round robin manner each time a create call is made; this provides a bit of load balancing across the connections for the resources created by each.

The PooledConnection class

Each time the PooledConnectionFactory instance is asked to create a connection it returns a PooledConnection instance that refers to one of the real connections in the pool. PooledConnection manages its own set of sessions in a session pool similar to the connection pool. Unlike the pooled connections, however, the sessions that are pooled in the connection are not shared; each call to create a session either returns a new session or a free one from the pool. The maximum number of sessions that PooledConnection will create is configurable, as is the behavior. When the maximum number has been reached, the code can either block any more sessions or it can throw an exception to indicate no more sessions can be created.

The PooledSession class

PooledSession also does its own sort of pooling for the MessageProducer objects it's asked to create. However, it is a bit different from the other object pools. Since a MessageProducer object can be created to be an anonymous producer and sent to any destination, the session creates a special wrapper that caches the destination your code asked it to associate with a JMS MessageProducer and always uses that cached destination to perform a send using a single MessageProducer instance, thereby saving the overhead of creating many different producer objects. While this still results in the allocation of objects on the client side, it saves the cost of the network overhead needed to register a new producer on the broker.

All of this may sound complicated but it's really not that bad. The classes in the pooling library behave pretty much the same as their pure JMS client counterparts. What you need to keep in mind is that when you close a PooledSession instance you close its cached producer, and when you close a PooledConnection instance you close all those PooledSession instances. The code that uses these types needs to ensure that it's taking advantage of the properties of the pooled library in a way that makes sense, otherwise it won't perform any better than the non-pooled JMS client code and could perform worse in some cases.

Our connection pooling example

The example application we ran earlier performed significantly better using the pooling library than it did using the plain ActiveMQ JMS client library code. Let's look at what the sample does and why it performs so much better.

The example application creates a ThreadPoolExecutorService instance that has a fixed thread pool of 10 and a PooledConnectionFactory instance which also sets its max connections to 10. The example then places 1,000 tasks into the executor; we show a portion of that code here:

connectionFactory = new ActiveMQConnectionFactory(connectionUri);
pooledFactory = new PooledConnectionFactory(connectionFactory);
…
ExecutorService service = Executors.newFixedThreadPool(10);

for (int i = 0; i < 1000; ++i) {
    service.execute(new Sender(factory.createConnection()));
}

As you can see, each of the queued tasks is given a connection from the ConnectionFactory instance we created. In the non-pooled case, we would be opening a large number of connections; this is not ideal as it involves a lot of network overhead adding each new connection to the broker. Each task in the executor then creates a session and sends some messages; let's take a look at that code now:

Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
for (int i = 0; i < 20; i++) {
    Destination destination = session.createTopic("TestTopic" + i);
    MessageProducer producer = session.createProducer(destination);
    producer.send(session.createMessage());
    producer.close();
}

As you can see, our task creates a session and then loops 20 times creating a MessageProducer instance each time in order to send to a specific destination on each send. In the non-pooled case, we'd be creating 20 MessageProducer objects in every loop and every task would create its own session. In the pooled case, though, we only create one session in each of the 10 connections and each session only creates one MessageProducer instance for each task as they are executed.

This example represents a worst-case scenario of bad JMS code that creates a lot of resources and destroys them without consideration for performance or resource utilization. It's because of this bad implementation that we see such a huge difference in performance between the pooled and non-pooled cases, but it makes clear the benefit of applying connection pooling in the right situations.

Once our example application finishes its work, it shuts down using the same style as our other examples by calling its after() method to clean up. Since we are using PooledConnectionFactory as opposed to the normal ActiveMQConnectionFactory, instance we need to ensure that those connections in the pool get shut down cleanly; we do that by calling the clear() method on PooledConnectionFactory; the code is shown here:

    public void after() throws Exception {
        pooledFactory.clear();
        broker.stop();
    }

Before we leave this section, it's good to note that we needed to update our standard Maven POM file for this project to make use of the ActiveMQ pooling library. We need to add one more dependency to our project. The XML is shown here:

    <dependency>
      <groupId>org.apache.activemq</groupId>
      <artifactId>activemq-pool</artifactId>
      <version>5.8.0</version>
    </dependency>

There's more...

You might have noticed that there was no mention of MessageConsumer objects being pooled by the ActiveMQ pooling library, and there's a good reason for that. Consumers, unlike connections, sessions, and MessageProducer resources, don't idle well. A MessageConsumer object continues to receive messages from ActiveMQ as long as its prefetch buffer is not full, and if it's sitting idle in a pool, those messages aren't being processed. And in the case of the queue, they aren't going to be sent to any other consumer until the consumer instance is closed. If we had several consumers sitting idle in our pool, they could collect a lot of messages that we might want to actually consume someplace else, causing our applications to behave in unexpected ways.

More information on configuring the pooling library

While the purpose of the pooling library is to keep our JMS resources open even when not being used, each of these resources does represent some overhead both on the client side and on the broker. We can configure our PooledConnectionFactory instance with an eviction time so that it will periodically close any resources that have sat idle in the pool for too long. Refer to the ActiveMQ API documentation at http://activemq.apache.org/maven/5.8.0/apidocs/org/apache/activemq/pool/PooledConnectionFactory.html for the various settings available for tuning PooledConnectionFactory.