使用MBean和Jolokia實現數據監控
靜態MBean的定義
通過MBean的定義可以在系統運行時,查看系統內部的數據或執行系統內部的功能,真正的使用過程中靜態MBean的使用是最多的。
定義的MBean必須實現以類名+MBean的接口
如下所示定義了一個MBean結構ShardInfo,所以爲了使ShardInfo能夠註冊進MBeanServer中,需要定義接口ShardInfoMBean,並實現。
@Getter
@Setter
public class ShardInfo extends AbstractMXBean implements ShardInfoMBean {
private String shardName;
protected ShardInfo(String mBeanName, String mBeanType, String mBeanCategory) {
super(mBeanName, mBeanType, mBeanCategory);
}
}
而mBeanName/mBeanType/mBeanCategory主要是對MBean進行分類,對應效果如圖:
實現的XXMBean接口中,以getXX方法確定可以查詢的字段
public interface ShardInfoMBean {
String getShardName();
}
如上面代碼所示,顯示的字段爲shardName。
可以觸發方法,也是XXMBean接口中定義的方法,實現即爲MBean中對應的實現
新增printShardName方法與實現
public interface ShardInfoMBean {
String getShardName();
void printShardName();
}
@Override
public void printShardName() {
System.out.println("ShardName:" + shardName);
}
手動觸發該方法:
方法執行完成:
MBean註冊
從上文已經知道對於靜態MBean如何定義,通過JDK自帶的ManagementFactory可以完成MBean的註冊。核心邏輯如下所示:
private boolean registerMBean() {
boolean registered = false;
try {
// Object to identify MBean
final ObjectName mbeanName = this.getMBeanObjectName();
log.debug("Register MBean {}", mbeanName);
// unregistered if already registered
if (server.isRegistered(mbeanName)) {
log.debug("MBean {} found to be already registered", mbeanName);
try {
unregisterMBean(mbeanName);
} catch (Exception e) {
log.warn("unregister mbean {} resulted in exception {} ", mbeanName, e);
}
}
server.registerMBean(this, mbeanName);
registered = true;
log.debug("MBean {} registered successfully", mbeanName.getCanonicalName());
} catch (Exception e) {
log.error("registration failed.", e);
}
return registered;
}
其中server是固定的:
private final MBeanServer server = ManagementFactory.getPlatformMBeanServer();
mbeanName全路徑名稱格式爲BASE_JMX_PREFIX:type=XX,catagory=XX,name=XX具體拼接代碼爲:
private ObjectName getMBeanObjectName() throws MalformedObjectNameException {
StringBuilder builder = new StringBuilder(BASE_JMX_PREFIX)
.append("type=").append(getMBeanType());
if (getMBeanCategory() != null) {
builder.append(",Category=").append(getMBeanCategory());
}
builder.append(",name=").append(getMBeanName());
return new ObjectName(builder.toString());
}
使用代碼查詢MBean屬性與執行方法
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
@Test
public void testGetShardInfoMBean() throws MalformedObjectNameException, IntrospectionException, InstanceNotFoundException, ReflectionException, AttributeNotFoundException, MBeanException {
ShardInfo shardInfo = new ShardInfo("sharInfo", "shard", "category1");
shardInfo.setShardName("sunquan");
shardInfo.register();
MBeanInfo mBeanInfo = server.getMBeanInfo(shardInfo.getMBeanObjectName());
//查詢屬性
Assert.assertEquals(mBeanInfo.getClassName(), ShardInfo.class.getName());
Assert.assertEquals("sunquan", server.getAttribute(shardInfo.getMBeanObjectName(), "ShardName"));
//執行方法
server.invoke(shardInfo.getMBeanObjectName(), "printShardName", null, null);
}
如何避免每個靜態MBean都要單獨定義接口
目前考慮有下列兩個方法:
- 開發MBean上的相關注解,觸發在編譯期其注入方法至接口類中
- 使用動態MBean實現