jms

Apache ActiveMQ Monitoring Tutorial

Apache ActiveMQ (AMQ) is an open source messaging server written in Java which implements JMS 1.1 specifications. In this example, I will explain how to monitor an AMQ server.
 
 
 
 
 
 
 
 
 

1. Introduction

Apache ActiveMQ (AMQ) is an open source messaging server written in Java which implements JMS 1.1 specifications. Most of the enterprise applications use AMQ as a building block for their MOM infrastructure, so it’s critical that the AMQ server functions as expected. In this example, I will explain how to monitor an AMQ server from these four aspects:

  • Java Virtual Machine (JVM)
  • AMQ Web Console
  • File System
  • Message Broker

2. Monitor ActiveMQ JVM

AMQ is a JVM. Oracle provides free monitoring tools to monitor the JVM’s CPU, Memory and threads.

Open Source JVM monitor tools:

3. Monitor ActiveMQ Web Console

AMQ provides a web console (Ex: http://localhost:8161/admin/index.jsp) to enable users to administrate and monitor the AMQ. The administrator can check any queue which has no consumer or has large amounts of pending messages.  AMQ also provides the XML feeds to retrieve the information from the queues. (Ex: http://localhost:8161/admin/xml/queues.jsp). Users can use a web application monitoring tool to monitor the web console index web page and the XML feed page.

Web application monitoring tools:

4. Monitor ActiveMQ File System

AMQ uses log4j to generate three log files under the data directory with the default settings.

  • activemq.log
  • audit.log
  • wrapper.log

The log file’s information and location can be changed by following these instructions. These log files grow as time goes by. Users can use a file system monitoring tool to monitor the disk space.

File system monitoring tools:

5. Monitor ActiveMQ Application

AMQ is a message broker which transfers the message from the sender to the receiver. Users monitor the AMQ broker to ensure that the messaging broker is in a healthy state.

AMQ broker monitoring tools:

6. Java Example

In this example, I will demonstrate how to build a simple Java scheduler application which monitors AMQ queues and then sends notifications when it detects a queue with no consumers or a large amount of pending messages.

6.1 Technologies used

The example code in this article was built and run using:

  • Java 1.8.101 (1.8.x will do fine)
  • Maven 3.3.9 (3.3.x will do fine)
  • Quartz 2.2.1 (2.x will do fine)
  • Eclipse Neon (Any Java IDE would work)

6.2 Data Model

AMQ provides a XML feeds to retrieve the information from the queues.

http://localhost:8161/admin/xml/queues.jsp

<queues>
	<queue name="test.queue">
		<stats size="0" consumerCount="0" enqueueCount="0" dequeueCount="0" />
		<feed>
			<atom>queueBrowse/test.queue?view=rss&amp;feedType=atom_1.0</atom>
			<rss>queueBrowse/test.queue?view=rss&amp;feedType=rss_2.0</rss>
		</feed>
	</queue>
</queues>

I create a ListOfActiveMqQueue to map to the root of the XML element queues.

ListOfActiveMqQueue.java

package jcg.demo.model;

import java.util.List;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "queues")
public class ListOfActiveMqQueue {
	private List queue;

	public List getQueue() {
		return queue;
	}

	public void setQueue(List queue) {
		this.queue = queue;
	}
}

I create a ActiveMqQueue to map to the child element queue.

ActiveMqQueue.java

package jcg.demo.model;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="queue")
@XmlAccessorType(XmlAccessType.FIELD)
public class ActiveMqQueue {

	@XmlAttribute
	private String name;
	private Stats stats;

	public String getName() {
		return name;
	}

	public Stats getStats() {
		return stats;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void setStats(Stats stats) {
		this.stats = stats;
	}
}

I create a Stats to map to the child element stats.

Stats.java

package jcg.demo.model;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Stats {

	@XmlAttribute
	private Integer size;
	@XmlAttribute
	private int consumerCount;
	@XmlAttribute
	private int enqueueCount;
	@XmlAttribute
	private int dequeueCount;

	public Integer getSize() {
		return size;
	}

	public int getConsumerCount() {
		return consumerCount;
	}

	public int getEnqueueCount() {
		return enqueueCount;
	}

	public int getDequeueCount() {
		return dequeueCount;
	}

	public void setSize(Integer size) {
		this.size = size;
	}

	public void setConsumerCount(int consumerCount) {
		this.consumerCount = consumerCount;
	}

	public void setEnqueueCount(int enqueueCount) {
		this.enqueueCount = enqueueCount;
	}

	public void setDequeueCount(int dequeueCount) {
		this.dequeueCount = dequeueCount;
	}
}

6.3 Monitor Service

Create a monitor service which monitors the given AMQ server’s queues and invokes the NotificationService to send an email when it detects that the queue has no consumers or there is a queue with too many pending messages.

ActiveMQMonitorService.java

package jcg.demo.service;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

import jcg.demo.model.ActiveMqQueue;
import jcg.demo.model.NotificationEmail;
import jcg.demo.model.ListOfActiveMqQueue;

public class ActiveMQMonitorService {

	private ActiveMqQueueTransformer transformer = new ActiveMqQueueTransformer();
	private NotificationService notService = new NotificationService();

	@SuppressWarnings("resource")
	public void monitorAndNotify(String brokerUrl, String username, String password, int maxPendingMessageLimit) {
		System.out.println("monitorAndNotify starts for " + brokerUrl);
		NotificationEmail email = dummyEmail();
		InputStream xmlFeedData = readXmlFeeds(brokerUrl, username, password);

		ListOfActiveMqQueue activeMqXmlData = transformer.convertFromInputStream(xmlFeedData);
		if (activeMqXmlData != null) {
			for (ActiveMqQueue queue : activeMqXmlData.getQueue()) {
				if (queue.getStats().getConsumerCount() == 0) {				
					email.setSubject("Must check activeMQ, queue: " + queue.getName() + " has no consumer.");
					notService.sendEmail(email);
				}
				else{
					int pendingMessageCounts = queue.getStats().getSize() - queue.getStats().getEnqueueCount();
					if( pendingMessageCounts > maxPendingMessageLimit){
						email.setSubject("Must check activeMQ, queue: " + queue.getName() + " has large pending message. ");
						notService.sendEmail(email);
					}
				}
			}
		}	
		System.out.println("monitorAndNotify completes for " + brokerUrl);
	}

	private InputStream readXmlFeeds(String brokerUrl, String username, String password) {
		try {
			URL url = new URL(brokerUrl);
			URLConnection uc = url.openConnection();

			String userpass = username + ":" + password;
			String basicAuth = "Basic " + javax.xml.bind.DatatypeConverter.printBase64Binary(userpass.getBytes());

			uc.setRequestProperty("Authorization", basicAuth);

			return uc.getInputStream();

		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return null;
	}
	
	private NotificationEmail dummyEmail() {
		NotificationEmail noConsumerEmail = new NotificationEmail();
		noConsumerEmail.setFromAddress("monitorApp@noreply.com");
		noConsumerEmail.setToAddress("activeMQServerMonitorTeam@test.com");
		noConsumerEmail.setBody("test email body message");
		return noConsumerEmail;
	}
}
  • Line 22: Read the AMQ queue data
  • Line 24: Transform the AMQ queue data from InputStream to ListOfActiveMqQueue
  • Line 27: Send notification when detects the queue has no consumer
  • Line 33: Send notification when detects the queue has large pending messages

6.4 Quartz Job

Create a Quartz job to monitor the AMQ server.

QuartzJob.java

package jcg.demo.scheduler.quartz2;

import java.time.LocalDateTime;
import java.util.List;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import jcg.demo.model.NotificationEmail;
import jcg.demo.service.ActiveMQMonitorService;
import jcg.demo.service.NotificationService;

/**
 * This class implements Quartz Job interface, anything you wish to be executed
 * by the Quartz Scheduler should be here it should invokes business class to
 * perform task.
 * 
 * @author Mary.Zheng
 *
 */
public class QuartzJob implements Job {

	private static final String LOGIN = "admin";
	private static final int MAX_PENDING_MESSAGE_SIZE_LIMIT = 10;
	private String brokerXmlUrl = "http://localhost:8161/admin/xml/queues.jsp";

	private ActiveMQMonitorService activeMqMonitorService = new ActiveMQMonitorService();

	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException {
		LocalDateTime localTime = LocalDateTime.now();
		System.out.println(Thread.currentThread().getName() + ": Run QuartzJob at " + localTime.toString());

		activeMqMonitorService.monitorAndNotify(brokerXmlUrl, LOGIN, LOGIN, MAX_PENDING_MESSAGE_SIZE_LIMIT);
	}
}

6.5 Monitor Application

Create a monitor application which runs every minutes to monitor the given AMQ server’s queues and sends notifications when it detects that the queue has no consumers or there is a queue with too many pending messages.

QuartzSchedulerApp.java

package jcg.demo.scheduler.quartz2;

import java.util.List;

import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;

/**
 * This application schedule a job to run every minute
 * 
 * @author Mary.Zheng
 *
 */
public class QuartzSchedulerApp {

	private static final String TRIGGER_NAME = "MyTriggerName";
	private static final String GROUP = "simple_Group";
	private static final String JOB_NAME = "someJob";
	private static Scheduler scheduler;

	public static void main(String[] args) throws Exception {
		System.out.println("QuartzSchedulerApp main thread: " + Thread.currentThread().getName());

		scheduler = new StdSchedulerFactory().getScheduler();
		scheduler.start();

		List currentJobs = scheduler.getCurrentlyExecutingJobs();
		for (JobExecutionContext currJob : currentJobs) {
			System.out.println("running job" + currJob.toString() + currJob.getJobDetail());
		}

		Trigger trigger = buildCronSchedulerTrigger();
		scheduleJob(trigger);

	}

	private static void scheduleJob(Trigger trigger) throws Exception {

		JobDetail someJobDetail = JobBuilder.newJob(QuartzJob.class).withIdentity(JOB_NAME, GROUP).build();

		scheduler.scheduleJob(someJobDetail, trigger);

	}

	private static Trigger buildCronSchedulerTrigger() {
		String CRON_EXPRESSION = "0 * * * * ?";
		Trigger trigger = TriggerBuilder.newTrigger().withIdentity(TRIGGER_NAME, GROUP)
				.withSchedule(CronScheduleBuilder.cronSchedule(CRON_EXPRESSION)).build();

		return trigger;
	}
}

6.6 Monitor Application execution

Start the AMQ server locally ad then execute the monitor application.

Output

QuartzSchedulerApp main thread: main
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
DefaultQuartzScheduler_Worker-1: Run QuartzJob at 2017-12-28T14:15:00.058
monitorAndNotify starts for http://localhost:8161/admin/xml/queues.jsp
NotificationService send email EmailNotification [ toAddress=activeMQServerMonitorTeam@test.com, subject=Must check activeMQ, queue: test.queue has no consumer., body=test email body message]
monitorAndNotify completes for http://localhost:8161/admin/xml/queues.jsp
DefaultQuartzScheduler_Worker-2: Run QuartzJob at 2017-12-28T14:16:00.002
monitorAndNotify starts for http://localhost:8161/admin/xml/queues.jsp
NotificationService send email EmailNotification [ toAddress=activeMQServerMonitorTeam@test.com, subject=Must check activeMQ, queue: test.queue has no consumer., body=test email body message]
monitorAndNotify completes for http://localhost:8161/admin/xml/queues.jsp

7. Summary

In this example, I outlined how to monitor an AMQ server from four aspects: JVM, AMQ web console, the data file system, and the message broker. I also built a simple Java monitor application to monitor the AMQ based on the web console. There are lots of monitoring tools in the market. Use your best judgement to pick the best monitoring tool for your business. Here are few items to consider when choosing a monitoring tool:

  • any security concern?
  • any impact to the AMQ server?
  • any interface to your notification tool?

8. References

9. Download the Source Code

This example consists of a Quartz scheduler to monitor an AMQ server based on the web console queue data.

Download
You can download the full source code of this example here: Apache ActiveMQ Monitoring Tutorial

Mary Zheng

Mary has graduated from Mechanical Engineering department at ShangHai JiaoTong University. She also holds a Master degree in Computer Science from Webster University. During her studies she has been involved with a large number of projects ranging from programming and software engineering. She works as a senior Software Engineer in the telecommunications sector where she acts as a leader and works with others to design, implement, and monitor the software solution.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button