使用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实现