1. Introduction
Message Driven Beans, also known as “MDB”, handle message processing in an asynchronous context. We can learn the basics of MDB in this article.
This tutorial will discuss some strategies and best practices to implement concurrency using Message Driven Beans.
If you want to understand more about the basics of concurrency using Java, you can get started here.
In order to better use MDBs and concurrency, there are some considerations to make. It’s important to keep in mind that those considerations should be driven by the business rules and the needs of our application.
2. Tuning the Thread Pool
Tuning the Thread Pool is probably the main point of attention. To make a good use of concurrency, we must tune the number of MDB instances available to consume messages. When one instance is busy handling a message, other instances are able to pick up the next ones.
The MessageListener thread is responsible to execute the onMessage method of an MDB. This thread is part of the MessageListener thread pool, which means that it’s pooled and reused over and over again. This pool also has a configuration that allows us to set the number of threads, which may impact the performance:
- setting a small pool size will cause messages to be consumed slowly (“MDB Throttling”)
- setting a very large pool size might decrease performance – or not even work at all.
On Wildfly, we can set this value by accessing the management console. JMS capability isn’t enabled on the default standalone profile; we need to start the server using the full profile.
Usually, on a local installation, we access it through http://127.0.0.1:9990/console/index.html. After that, we need to access Configuration / Subsystems / Messaging / Server, select our server and click “View”.
Choose the “Attributes” tab, click on “Edit” and change the value of “Thread Pool Max Size”. The default value is 30.
3. Tuning Max Sessions
Another configurable property to be aware of is Maximum Sessions. This defines the concurrency for a particular listener port. Usually, this defaults to 1 but increasing it can give more scalability and availability to the MDB application.
We can configure it either by annotations or .xml descriptors. Through annotations, we use the @ActivationConfigProperty:
@MessageDriven(activationConfig = { @ActivationConfigProperty( propertyName=”maxSession”, propertyValue=”50” ) })
If the chosen method of configuration is .xml descriptors we can configure maxSession like this:
<activation-config> <activation-config-property> <activation-config-property-name>maxSession</activation-config-property-name> <activation-config-property-value>50</activation-config-property-value> </activation-config-property> </activation-config>
4. Deployment Environment
When we have a requirement for high availability, we should consider deploying the MDB on an application server cluster. Thus, it can execute on any of the servers in the cluster and many application servers can invoke it concurrently, which also improves scalability.
For this particular case, we have an important choice to make:
- make all servers in the cluster eligible to receive messages, which allows the use of all of its processing power, or
- ensure message processing in a sequential manner by allowing just one server to receive them at a time
If we use an enterprise bus, a good practice is to deploy the MDB to the same server or cluster as the bus member to optimize the messaging performance.
5. Message Model and Message Types
Although this isn’t as clear as just setting another value to a pool, the message model and the message type might affect one of the best advantages of using concurrency: performance.
When choosing XML for a message type, for instance, the size of the message can affect the time spent to process it. This is an important consideration especially if the application handles a large number of messages.
Regarding the message model, if the application needs to send the same message to a lot of consumers, a publish-subscribe model might be the right choice. This would reduce the overhead of processing the messages, providing better performance.
To consume from a Topic on a publish-subscribe model, we can use annotations:
@ActivationConfigProperty( propertyName = "destinationType", propertyValue = "javax.jms.Topic")
Again, we can also configure those values in a .xml deployment descriptor:
<activation-config> <activation-config-property> <activation-config-property-name>destinationType</activation-config-property-name> <activation-config-property-value>javax.jms.Topic</activation-config-property-value> </activation-config-property> </activation-config>
If sending the very same message to many consumers isn’t a requirement, the regular PTP (Point-to-Point) model would suffice.
To consume from a Queue, we set the annotation as:
@ActivationConfigProperty( propertyName = "destinationType", propertyValue = "javax.jms.Queue")
If we’re using .xml deployment descriptor, we can set it:
<activation-config> <activation-config-property> <activation-config-property-name>destinationType</activation-config-property-name> <activation-config-property-value>javax.jms.Queue</activation-config-property-value> </activation-config-property> </activation-config>
6. Conclusion
As many computer scientists and IT writers already stated, we no longer have processors’ speed increasing on a fast pace. To make our programs work faster, we need to work with the higher number of processors and cores available today.
This article discussed some best practices for getting the most out of concurrency using MDBs.