JMX Monitoring Service

 1.Purpose of Monitoring service

   JMX Monitoring service provide a mechanism of monitoring your observable object with a predefined observable attribute. In the simplest case, you might want to get an email to be informed about the state of application especally when it crashes, then you can take some corrective actions. Or you might also want to be informed when some critical files change.

 

2.JMX Monitoring Service architecture

   Let's get a overview of JMX Monitoring Service architecture first:



  As the picture shows above, we can see that JMX Monitoring Service provide three types of monitor:StringMonitor,GaugeMonitor and CounterMonitor. And they are all standard MBean. Later I will cover them in detail one by one with the following aspects:

  • What is it for?
  • How to use monitor of this type?
  • Its Notification type.

 Before we examine them one by one, I will walk you through its parent class Monitor and their common interface MonitorMBean first.

 

 Now let's take a look at the class diagram of MonitorMBean and see what we can get:

 

 

  1. start()&stop() - to start/stop the monitor.
  2. addObservedObject(ObjectName),removeObservedObject(ObjectName),containsObservedObject(ObjectName) and getObservedObjects() - these four methods provide a mechanism of handling the observed object.
  3. setObservedAttribute(String)&getObservedAttribute() - tell the monitor the attribute to observe.
  4. setGranularityPeriod(long)&getGranularityPeriod() - sets or return the observation intervals of the monitor.

    In brief, MonitorMBean acts like a small container, it hold observed objects, set the observed attribute and determins how long it will check the observed attribute. MonitorMBean show us a big picture of monitor service in JMX.

 

    And about Monitor, since it implements MonitorMBean and extends NotificationBroadcasterSupport, therefore, it is not only a monitor but also a Notification broadcaster. And right here what I want to show you is that how monitor works, that is, how it detect deserved attribute changes and how it send notification out when deserved attribute changes. Before we start our trip, I assume you have the basic knowledge of the new java concurrency package.

 

   From the picture above, we can see that monitor uses the following four classes to implement its monitor service.

  • ScheduledExecutorService indicates a daemon thread here while ExecutorService indicates a thread pool.
  • ScheduledExecutorService use ScheduleTask as its runnable command while ExecutorService take MonitorTask as its runnable command.

   Actually Monitor starts a deamon thread to monitor its observed objects.

 

#Monitor.class
private static final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new DaemonThreadFactory("Scheduler")); 

  

 private static class DaemonThreadFactory implements ThreadFactory {
        ......

        public Thread newThread(Runnable r) {
            Thread t = new Thread(group,
                                  r,
                                  namePrefix +
                                  threadNumber.getAndIncrement() +
                                  nameSuffix,
                                  0);
            t.setDaemon(true);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }

 

    As we can see from DaemonFactory, it newes a daemon thread with the NORM_PRIORITY. Every Monitor has its own daemon thread. And the parameter Runnable r of this newThread(Runnable) method is actually a SchedulerTask, when we walk deeply into the code, we find that SchedulerTask is only a wrapper of MonitorTask, MonitorTask is the task which really starts the monitoring logic.  When we start the monitor, it will start the daemon thread within its doStart() method:

 

#Monitor.class  
private final MonitorTask monitorTask = new MonitorTask();   
  
private final SchedulerTask schedulerTask = new SchedulerTask(monitorTask);  

void doStart() {   
  
             ......   
  
             // Start the scheduler.   
            schedulerFuture = scheduler.schedule(schedulerTask,   
                                                 getGranularityPeriod(),   
                                                 TimeUnit.MILLISECONDS);   
  }   
}  

  

   From the code above, we know that schedulerTask will be executed only once after granularityPeriod milliseconds' delay(scheduler is a single-threaded executor that can schedule commands to run after a given delay). Some people might be wonderring how this single-threaded executor run commands periodically, the answer is in the MonitorTask.

 

 public void run() {
            ...
            AccessController.doPrivileged(new PrivilegedAction() {
                public Object run() {
                    if (Monitor.this.isActive()) {
                        final int an[] = alreadyNotifieds;
                        int index = 0;
                        for (ObservedObject o : Monitor.this.observedObjects) {
                            if (Monitor.this.isActive()) {
                                Monitor.this.monitor(o, index++, an);
                            }
                        }
                    }
                    return null;
                }
            }, Monitor.this.acc);
            synchronized (Monitor.this) {
                if (Monitor.this.isActive() &&
                    Monitor.this.schedulerFuture == sf) {
                    Monitor.this.monitorFuture = null;
                    Monitor.this.schedulerFuture =
                        scheduler.schedule(Monitor.this.schedulerTask,
                                           Monitor.this.getGranularityPeriod(),
                                           TimeUnit.MILLISECONDS);
                }
            }
        }

 

   In MonitorTask, it first invoke Monitor#monitor() method to detect observed attribute changes and send notification, then it will invoke the ScheduledExecutorService#schedule(Runnable,long,TimeUnit) again to reach the goal of running schedulerTask periodically. But I am wondering why ScheduledExecutorService#schedule(Runnable,long,TimeUnit) is being used here to reach the goal of running schedulerTask periodically? Can we just use ScheduledExecutorService#scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit) instead in Monitor#doStart() method?Not like the ScheduledExecutorService#schedule method, ScheduledExecutorService#scheduleAtFixedRate can schedule commands to run periodically.

 

#Monitor.class  
private final MonitorTask monitorTask = new MonitorTask();   
  
private final SchedulerTask schedulerTask = new SchedulerTask(monitorTask);  

void doStart() {   
  
             ......   
  
             // Start the scheduler.   
            // schedulerFuture = scheduler.schedule(schedulerTask,   
            //                                      getGranularityPeriod(),   
            //                                      TimeUnit.MILLISECONDS);  

            //use this one instead of the above one
            schedulerFuture = scheduler.scheduleAtFixedRate  
            (schedulerTask,   initialDelay time,
                                        getGranularityPeriod(),   
                                        TimeUnit.MILLISECONDS);    
  }   
}  

 

   The only one reason I can see here is about granularityPeriod variable defined in Monitor class, since granularityPeriod can be changed on the runtime,if we use  ScheduledExecutorService#scheduleAt-FixedRate instead, the change of granularity doesn't make sence any more. And once the period has been setted for ScheduledExecutorService#scheduleAtFixedRate,it can't be changed any longer.

 

   The following sequence diagram could give you a better understand of what I said above:

   

 

 

  As we saw above, we know when MonitorTask executes, it invokes Monitor#monitor() to observe the observed attribute. Let's take a look at what happens in the monitor() method.

    

 

    From the picture above, first, monitor will get the observed attribute value of the observed object, then build notification according to different monitor type. Take string monitor for example, it will compare the observed attribute value with the value setted by setStringToCompare() mothod, finally, send the notifications if any. Actually buildAlarmNotification() is a template method implemented by subclasses of monitor.

 

   And now, I am sure you must have known how monitor works. So, let's check the JMX monitors one by one now.

 

 

StringMonitor:

  • Purpose

          As its name suggests, StringMonitor is used to monitor a attribute of a string type in your observable object.

  •  Using String Monitor

           Before using String Monitor, we need to configure three values since String Monitor's behavior is based on them, and these three values can be setted by the following methods:

 

Method NameDescription   
setStringToCompare(String)set the internal string that will be used to compare with the attribute value of observed object.
setNotifyMatch(boolean)tells monitor to send notification if the attribute value you apply to the observed object matches the internal string setted by setStringToCompare(string)
setNotifyDiffer(boolean)tells monitor to send notification if the attribute value you apply to the observed object does not match the internal string setted by setStringToCompare(string)

 

  • String Monitor Notification Type

          StringMonitor supports two Notification types: Match type and Differ type.

 

 

Notification TypeDescription
jmx.monitor.string.matchesthat the attribute value you apply to the observable object is the matching value.
jmx.monitor.string.differsindicates that the attribute value you apply to the observable object is different from the matching value

        

         Note: Monitor will send notification only when the observed attribute transitions from a match to a non-match or vice versa. It wont continuously send notification if the value always matches or always differs.

 

 

GaugeMonitor:

  • Purpose

          Gauge Monitor is used to monitor a range of values because it is observing an attribute that possibly cross the predefined low threshold or the predefined high threshold. 

         

  • Using Gauge Monitor

 

Method NameDescription
setThresholds(Number highValue, Number lowValue)Sets the high and the low threshold values of the gauge monitor
setNotifyHigh(boolean)tells monitor to send notification if the observed attribute  crosses the high threshold of monitor
setNotifyLow(boolean)tells monitor to send notification if the observed attribute goes below the high threshold of monitor
setDifferenceMode(boolean)if true, uses the substraction to calculate its derived gauge

 

  • Gauge Monitor Notification Type

           In addition to the common notification type, there are still two notification types for gauge monitor.

 

 

Notification TypeDescription
jmx.monitor.gauge.hightells monitor to send High type notification once the observed attribute crosses the high threshold of monitor
jmx.monitor.gauge.lowtells monitor to send Low type notification once the  observed attribute goes below the high threshold of monitor

 

Counter Monitor:

  • Purpose

          Since it is counter monitor, the observed attribute is assumed to be positive and to have an increasing value. And counter monitor is used to check if the observed attribute reaches the maximum value, if it reaches, counter monitor will send out a notification to notify this event.

  • Using Counter Monitor

          The following table lists the methods of counter monitor, which should be used to describe its behavior.

 

MethodDescription
setInitThreshold(Number)set the initial threshold for this monitor,which is used to compare with the observed attribute value.
setOffset(Number)set the offset value of the monitor
setModulus(Number)set the maximum value for this monitor
setNotify(boolean)if true, tells monitor to send out notifications when the observed attribute value reaches its counter
setDifferenceMode(boolean)Same as gauge monitor, if ture,it will recalcute its derived gauge

 

As we can see from the above table, the initThreshold is used to compare with the observed attribute value, but what are the offset and modulus for? Offset is added to the initThreshold value when the observed attribute value exceeds the initThreshod and counter monitor will keep adding its offset value to the threshold until it is greater than the observed attribute. If the modulus has been setted, and if adding the offset exceeds the modulus,the initThreshold will reset to its original value.

  • Counter Monitor Notification Type

          Like the gauge monitor, counter monitor not only shares the common notification type,'jmx.monitor.error.threshold', but also adds a new notification type,'jmx.monitor.counter.threshold'

 

Notification TypeDescription
jmx.monitor.error.threshodindicate the monitor's threshold,offset or modulus is not the same type as the observed counter attribute value
jmx.monitor.counter.thresholdindicate the observed attribute value has been reached the monitor's specified threshold.

 

 

3.Monitor Example

   We have discussed each Monitor MBean's behavior and important methods. Now it is time to write some code. First, we need an observed object.

 

 

public interface ObservableObjectMBean {
	//for string monitor
	public void setName(String name);
	public String getName();
	
	//for gauge monitor
	public void setRange(Float range);
	public Float getRange();
	
	//for counter monitor
	public void setCounter(int counter);
	public int getCounter();
}

 

  

public class ObservableObject implements ObservableObjectMBean {
	private String name;
	
	private Float range;
	
	private int counter;
	
	public Float getRange() {
		return range;
	}

	public void setRange(Float range) {
		this.range = range;
	}

	public int getCounter() {
		return counter;
	}

	public void setCounter(int counter) {
		this.counter = counter;
	}

	public String getName() {
		return name;
	}

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

}

   

    And then our Monitor Agent.

  

public class MonitorAgent implements NotificationListener{

	public MonitorAgent() throws Exception
	{
		MBeanServer server = MBeanServerFactory.createMBeanServer();
		// new a adaptor and register it in the server
		HtmlAdaptorServer adaptor = new HtmlAdaptorServer();
		adaptor.setPort(8888);
		ObjectName adaptorName = new ObjectName("Monitor:name=adaptor");
		server.registerMBean(adaptor, adaptorName);
		
		//new a observable object
		ObjectName observableObjName=new ObjectName("Monitor:name=observable_obj");
		ObservableObjectMBean observableObj=new ObservableObject();
		server.registerMBean(observableObj, observableObjName);
		
		
		//new a String monitor
		StringMonitor stringMonitor=new StringMonitor();
		ObjectName monitorObjName=new ObjectName("Monitor:name=StringMonitor");
		stringMonitor.setObservedAttribute("Name");
		stringMonitor.setStringToCompare("kevinWang");
		stringMonitor.setNotifyDiffer(true);
		stringMonitor.setNotifyMatch(true);
		stringMonitor.addObservedObject(observableObjName);
		stringMonitor.start();
		server.registerMBean(stringMonitor,monitorObjName);
		
		
		//new a gauge monitor
		GaugeMonitor gaugeMonitor=new GaugeMonitor();
		ObjectName gaugeObjName=new ObjectName("Monitor:name=GaugeMonitor");
		gaugeMonitor.setObservedAttribute("Range");
		gaugeMonitor.setDifferenceMode(false);
		gaugeMonitor.setThresholds(5.5f, 2.1f);
		gaugeMonitor.setNotifyHigh(true);
		gaugeMonitor.setNotifyLow(true);
		gaugeMonitor.addObservedObject(observableObjName);
		gaugeMonitor.start();
		server.registerMBean(gaugeMonitor, gaugeObjName);
		
		//new a counter monitor
		CounterMonitor counterMonitor=new CounterMonitor();
		ObjectName counterObjName=new ObjectName("Monitor:name=CounterMonitor");
		counterMonitor.addObservedObject(observableObjName);
		counterMonitor.setObservedAttribute("Counter");
		counterMonitor.setInitThreshold(3);
		counterMonitor.setOffset(1);
		counterMonitor.setModulus(6);
		counterMonitor.setNotify(true);
		counterMonitor.start();
		server.registerMBean(counterMonitor, counterObjName);
		
		//add listener
		server.addNotificationListener(monitorObjName, this, null, null);
		server.addNotificationListener(gaugeObjName, this, null, null);
		server.addNotificationListener(counterObjName, this, null, null);
		
		adaptor.start();
	}
	public static void main(String[] args) throws Exception{
		MonitorAgent agent=new MonitorAgent();
	}

	public void handleNotification(Notification notification, Object handback) {
		System.out.println(notification.getType());
	}

}

 

   After we run Monitor Agent, you can get access to http://localhost:8888 and test them.

  

   For String Monitor, input one string that is different from 'kevinWang' for the observed attribute 'name', for Gauge Monitor, input a float number that is not btw 2.1f and 5.5f for the observed attribute 'range', and for Counter Monitor, input a int number that is over the initThreshold for the observed attribute 'counter'. The following is the snapshot of inputted parameter.

  

 

   Then click the 'Apply' button, you will see the output as below:

 

   jmx.monitor.string.differs
   jmx.monitor.gauge.high
   jmx.monitor.counter.threshold

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值