4.0 通知
JMX API定义了一种机制,使得MBeans能够生成通知Notificationn,比如通知一个状态改变、一个检测到的事件或者问题。通知的作用是主动通知远程客户端。例如我们的程序出现了异常,或者CPU使用率过高,或者程序出现了死锁等一系列问题。这个时候我们希望程序能主动将这些问题发送给远程客户端,将这些问题记录下来,或者执行一些其他的报警操作。
通知的类型
在JMX中,通知通过类 javax.management.Notification
来表示。因为通知有很多种,所以Notification有很多子类,用于细分不同类型的通知,如下:
可以看到细分的通知种类有很多,最容易理解的是 AttributeChangedNotification
。在前面我们已经讲解过MBean中可以定义属性和操作,表示的就是当属性发生变化时,我们可以发出的通知类型。
每个通知都有一个源头。源头就是产生该通知的MBean的对象名。一个MBean只要检测条件的变化,就可以发送对应的通知。
每个通知都有一个序列号编号。当通知的接收顺序很重要 并且 以错误的顺序处理通知可能会有风险时,该编号可以用于对来自相同通知源头的通知进行排序。序列编号可以是0,但是为每个来自特定MBean的通知 设置 递增的号码 会更好。举例来说,看一下AttributeChangedNotification
的构造方法:
/** * Constructs an attribute change notification object. * In addition to the information common to all notification, the caller must supply the name and type * of the attribute, as well as its old and new values. * * @param source The notification producer, that is, the MBean the attribute belongs to. * @param sequenceNumber The notification sequence number within the source object. * @param timeStamp The date at which the notification is being sent. * @param msg A String containing the message of the notification. * @param attributeName A String giving the name of the attribute. * @param attributeType A String containing the type of the attribute. * @param oldValue An object representing value of the attribute before the change. * @param newValue An object representing value of the attribute after the change. */ public AttributeChangeNotification(Object source, long sequenceNumber, long timeStamp, String msg, String attributeName, String attributeType, Object oldValue, Object newValue)
source指的是生成这个通知的MBean实例,sequenceNumber指的是通知的序号。
通知的发送
很容易想到,既然我们MBean可以发送通知,那么对应的,肯定有一个通知的接受方。不过目前我们不讨论通知的接收方,只考虑如何发送通知。
通常情况下,通知的发送方和通知的接收方是不在同一台物理机器上的,所以发送通知这个操作必然是需要进行网络传输的,而且我们还要监听通知的产生等等方面的问题。幸运的是,在JMX中,我们并不需要进行网络编程来发送通知。在JMX中,有一个NotificationBroadcasterSupport
类,其已经帮助我们实现了发送一个通知需要考虑的各种问题:监听通知的产生、通知的发送等...。我们的MBean只需要继承这个类,就可以具备发送通知的功能。
一个MBean要想生成通知,那么它必须实现接口 NotificationEmitter
或者扩展
NotificationBroadcasterSupport
。我们要关注的是以下两个方法:
public class NotificationBroadcasterSupport implements NotificationEmitter { ... public MBeanNotificationInfo[] getNotificationInfo(){...} public void sendNotification(Notification notification){...} ... }
sendNotification方法:
发送一条通知,你需要构造javax.management.Notification
或其子类(比如AttributeChangedNotification
)的一个实例传入即可。
getNotificationInfo方法:
前面提到的Notification的子类有很多种,这个方法是告诉JMX框架我们的MBean会发送哪几种类型的通知。如果我们在MBean中发送的通知类型,但是没有在这里声明,那么就是非法的。
这个方法的返回值是MBeanNotificationInfo[]
,每个通知类型对应数组中的一个元素。MBeanNotificationInfo对象中维护的信息包括:通知类型,通知对应的java类的全名,以及一些描述信息等。
通知的实现:
在Standard MBeans课程中描述的Hello
MBean实现实际上已经实现了通知机制。只是,为了简便起见,相关代码在该课程中省略了。 Hello
的完整代码如下:
package com.tianshouzhi.jmx.notification; import javax.management.AttributeChangeNotification; import javax.management.MBeanNotificationInfo; import javax.management.Notification; import javax.management.NotificationBroadcasterSupport; public class Hello extends NotificationBroadcasterSupport implements HelloMBean { public void sayHello() { System.out.println("hello, world"); } public int add(int x, int y) { return x + y; } public String getName() { return this.name; } public int getCacheSize() { return this.cacheSize; } public synchronized void setCacheSize(int size) { int oldSize = this.cacheSize; this.cacheSize = size; System.out.println("Cache size now " + this.cacheSize); //构建通知 Notification n = new AttributeChangeNotification(this, sequenceNumber++, System.currentTimeMillis(), "CacheSize changed", "CacheSize", "int", oldSize, this.cacheSize); //发送通知 sendNotification(n); } //返回这个MBean将会发送的通知类型信息 @Override public MBeanNotificationInfo[] getNotificationInfo() { String[] types = new String[] { AttributeChangeNotification.ATTRIBUTE_CHANGE }; String name = AttributeChangeNotification.class.getName(); String description = "An attribute of this MBean has changed"; MBeanNotificationInfo info = new MBeanNotificationInfo(types, name, description); return new MBeanNotificationInfo[] { info }; } private final String name = "Reginald"; private int cacheSize = DEFAULT_CACHE_SIZE; private static final int DEFAULT_CACHE_SIZE = 200; private long sequenceNumber = 1; }
Hello
MBean扩展了NotificationBroadcasterSupport
类。NotificationBroadcasterSupport
实现了 NotificationEmitter
接口。
各个操作和各个属性的设置方式和标准MBean那个例子相同,唯一的例外是CacheSize
的属性的setter方法现在定义了一个 oldSize
值。该值记录了在对CacheSize
属性执行set操作之前,CacheSize
属性的值。
通知从JMX class AttributeChangeNotification
构造而来,名字为 n,AttributeChangeNotification扩展了
javax.management.Notification
。我们在方法setCacheSize()
中构造该通知,携带下列信息。该信息被作为参数传给 AttributeChangeNotification
。
通知源头的对象名,也就是
Hello
MBean,用this代表它。一个序列号,也就是
sequenceNumber
,它被设置为1,然后逐渐递增。一个时间戳。
通知消息的内容。
被改变的属性的名字,在这里,为
CacheSize
被改变的属性的类型。
被改变的属性的旧值,在这里,为
oldSize
被改变的属性的新值,在这里,为
this.cacheSize
该通知,n,然后被传递给 NotificationBroadcasterSupport.sendNotification()
方法。
最后,定义了 MBeanNotificationInfo
实例,它用于描述该MBean为特定类型的通知 生成的不同通知的特征。
关于MBeanNotificationInfo的提示
"public MBeanNotificationInfo[] getNotificationInfo()"方法来自顶层接口 javax.management.MBeanNotificationInfo[] getNotificationInfo()。只在注册该MBean的时候,调用一次,用于获取该MBean为特定类型的通知 生成的不同通知的特征。
如果想要接收某种类型的消息,那么需要到 MBean Server中订阅(JMX Client也可以到MBean Server中订阅)。
本例中,该方法实现为:
public MBeanNotificationInfo[] getNotificationInfo() { String[] types = new String[] { AttributeChangeNotification.ATTRIBUTE_CHANGE }; String name = AttributeChangeNotification.class.getName(); String description = "An attribute of this MBean has changed"; // MBeanNotificationInfo info = new MBeanNotificationInfo( types, name, description); return new MBeanNotificationInfo[] { info }; }
这个方法只体现了一个类型的通知,AttributeChangeNotification.ATTRIBUTE_CHANGE。没有体现出多种类型的通知。实际应用中,该方法可能是这样的:
public MBeanNotificationInfo[] getNotificationInfo() { String[] types = new String[] { AttributeChangeNotification.ATTRIBUTE_CHANGE, “XXX”, "YYY" ... }; String name = AttributeChangeNotification.class.getName(); String description = "An attribute of this MBean has changed"; // MBeanNotificationInfo info1 = new MBeanNotificationInfo( types, name, description); MBeanNotificationInfo info2 = new MBeanNotificationInfo(...) return new MBeanNotificationInfo[] { info1, info2, info3, ... }; }
运行该MBean Notification例子
1、运行程序。
2、使用JConsole连接。查看MBeans标签页。
可以看到名称为Hello的MBean多了一个Notifications。
3、点击Subscribe按钮,表示要订阅通知。
4、修改CacheSize的值
修改之后点击refresh按钮。
5、刷新之后,我们可以看到Notifications[0]变成了Notifications[1],表示接受到了一条消息。