Blogs
Texto
Esta entrada de blog también está disponible en Español
Working with Java applications that use native thread pools, one
of the limitations we may encounter is the lack of built-in tools to
monitor the behavior of these pools. Although libraries such as
ThreadPoolExecutor
in Java offer powerful functionalities
to manage threads, they do not directly expose metrics for
monitoring tools such as JMX.
In this blog, I share how to leverage MBeans to expose custom thread pool metrics. This will allow us to monitor in real time aspects such as the number of active threads, the size of the pool, and the size of the task queue for them.
Why use MBeans?
MBeans (Managed Beans) are a mechanism built into Java (standard) for exposing information about the behavior and state of an application. Using MBeans has several advantages:
-
Real-Time Monitoring: We can check the metrics from tools such as JConsole, VisualVM, Glowroot, Dynatrace, etc.
-
Flexibility: It allows us to expose metrics from any component that does not have native support for it.
-
Interoperability: It integrates with advanced monitoring systems such as Prometheus or Grafana, which are a standard in the Cloud paradigm.
Implementing MBeans for Thread Pools
Let's build an example where we register an MBean that monitors
a ThreadPoolExecutor
. This MBean will expose three key metrics:
-
Active Threads: Number of parallel threads currently executing tasks.
-
Pool Size: Total number of threads in the pool.
-
Queue Length: Number of tasks waiting in the queue.
Code
The following code snippet implements the registration of an MBean to monitor the thread pool in a portlet:
private static final String MBEAN_NAME = "com.liferay.mcv.example.parallel:type=ThreadPool,name=ParallelThreadsPool";
@Override
public void init() throws PortletException {
super.init();
registerMBean();
}
@Override
public void destroy() {
super.destroy();
executorService.shutdown();
}
private void registerMBean() {
try {
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName objectName = new ObjectName(MBEAN_NAME);
ThreadPoolMetrics metrics = new ThreadPoolMetrics(executorService);
if (!mBeanServer.isRegistered(objectName)) {
mBeanServer.registerMBean(metrics, objectName);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static class ThreadPoolMetrics implements ThreadPoolMetricsMBean {
private final ThreadPoolExecutor executor;
public ThreadPoolMetrics(ThreadPoolExecutor executor) {
this.executor = executor;
}
@Override
public int getActiveThreads() {
return executor.getActiveCount();
}
@Override
public int getPoolSize() {
return executor.getPoolSize();
}
@Override
public int getQueueSize() {
return executor.getQueue().size();
}
}
public interface ThreadPoolMetricsMBean {
int getActiveThreads();
int getPoolSize();
int getQueueSize();
}
}
Code Analysis
-
Registering the MBean: The
registerMBean
method uses the JVM'sMBeanServer
to register an object that exposes thread pool metrics. This allows monitoring tools to access these metrics. -
MBean Interface: The
ThreadPoolMetricsMBean
interface defines the methods that the MBean will expose. Each method corresponds to a specific metric. -
Exposed Metrics:
-
getActiveThreads: Returns the number of threads currently running.
-
getPoolSize: Returns the total size of the pool.
-
getQueueSize: Returns the number of pending tasks in the queue.
-
In the following GitHub repository you can find the complete example with which you can deploy the example portlet in Liferay DXP 7.4 Q4.0:
Checking the metrics
In the portlet example, I implement an action to add 10 threads with each invocation, which print a message on the screen for 30 seconds. A maximum of 100 threads will be allowed to be added and from there, they will be queued up to a maximum of 200.
Once the example is explained, after registering the MBean (during the Portlet deployment), we can use tools such as Glowroot or VisualVM to inspect its metrics by doing the following steps:
-
We connect to the JVM where our Java application is running (like Liferay DXP or your SpringBoot app ;) )
-
View the "MBeans" and locate the MBean under the name:
com.liferay.mcv.example.parallel:type=ThreadPool,name=ParallelThreadsPool
(With Glowroot it will be necessary to add the MBean to the configuration in gauges previously, from the Console) -
Playing with the portlet action, you can view the metrics displayed in real time and create alerts based on this metrics:
Conclusion
Implementing MBeans to monitor thread pools is an elegant and efficient solution when working with mechanisms that do not expose metrics by default. This technique is not only useful for thread pools, but also for any shared resource that needs to be monitored.
With this solution, you can improve the visibility of your applications and anticipate potential bottlenecks, taking the monitoring of your systems to a new level.