Mbean在druid中的应用
我们可以看到在druid中很多地方都存在着Mbean的实现,比如在datasource的类中:
DruidDataSource extends DruidAbstractDataSource implements DruidDataSourceMBean
复制代码
之前也没有用过mbean相关,这里首先去了解了一下MBean, MBean就是一种规范的JavaBean,通过集成和实现一套标准的Bean接口,这种叫MBean,MBean注册到MBeanServer中。之后将被MBeanServer中注册过的Adapter将MBean的属性和方法展示给用户。
注册MBean了以后,就可以在jconsole中看到mbean相关的方法,这里我首先实现了一个自己的demo
public interface HelloMBean {
public void sayHello();
public int add(int x, int y);
public String getName();
public int getCacheSize();
public void setCacheSize(int size);
}
复制代码
public class Hello implements HelloMBean {
private final String name = "Reginald";
private int cacheSize = DEFAULT_CACHE_SIZE;
private static final int DEFAULT_CACHE_SIZE = 200;
@Override
public void sayHello() {
System.out.println("hello, world");
}
@Override
public int add(int x, int y) {
return x + y;
}
@Override
public String getName() {
return this.name;
}
@Override
public int getCacheSize() {
return this.cacheSize;
}
@Override
public synchronized void setCacheSize(int size) {
this.cacheSize = size;
System.out.println("Cache size now " + this.cacheSize);
}
}
复制代码
实现上述的实现类和监控类以后,在启动程序中添加部分注册代码就可以把之前写的MBean注册上了:
public static void main(String[] args) throws MalformedObjectNameException, NotCompliantMBeanException, InstanceAlreadyExistsException, MBeanRegistrationException {
SpringApplication.run(CacheApplication.class, args);
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName name = new ObjectName("io.kimmking.cache.service:type=Hello");
Hello mbean = new Hello();
mbs.registerMBean(mbean, name);
}
复制代码
在jconsole中就可以看到注册完成的属性。
另外一种情况可以使用注解来注册mbean,三个注解: @ManagedResource @ManagedAttribute 和 @ManagedOperation
途 | Commons Attributes属性 | JDK 5.0注解 | 属性/注解类型 |
---|---|---|---|
将类的所有实例标识为JMX受控资源 | ManagedResource |
@ManagedResource |
Class 类 |
将方法标识为JMX操作 | ManagedOperation |
@ManagedOperation |
Method方法 |
将getter或者setter标识为部分JMX属性 | ManagedAttribute |
@ManagedAttribute |
Method (only getters and setters) 方法(仅getters和setters) |
定义操作参数说明 | ManagedOperationParameter |
@ManagedOperationParameter 和 @ManagedOperationParameters |
Method 方法 |
举例设置:
@ManagedResource(objectName="bean:name=TestBean", description="My Managed Bean")
@Component("annotationHelloMBean")
public class AnnotationHelloMBean {
private final String name = "Reginald";
private int cacheSize = DEFAULT_CACHE_SIZE;
private static final int DEFAULT_CACHE_SIZE = 200;
@ManagedAttribute(description="The hello Attribute")
public void sayHello() {
System.out.println("hello, world");
}
@ManagedOperation(description="Add two numbers")
@ManagedOperationParameters({
@ManagedOperationParameter(name = "x", description = "The first number"),
@ManagedOperationParameter(name = "y", description = "The second number")})
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) {
this.cacheSize = size;
System.out.println("Cache size now " + this.cacheSize);
}
}
复制代码
在启动类里注入测试的方法:
ApplicationContext applicationContext=SpringApplication.run(CacheApplication.class, args);
AnnotationHelloMBean annotationHelloMBean= (AnnotationHelloMBean)applicationContext.getBean("annotationHelloMBean");
复制代码
然后启动应用就可以在jconsole中看到刚刚写好的方法的Mbean
在druid中的实现:
在datasource里面的init方法中,实现了注册:
initedLatch.await();
init = true;
initedTime = new Date();
registerMbean();
复制代码
注册方法如下,首先用 doPrivileged绕过权限检查。 启动一个线程调用DruidDataSourceStatManager
来启动mbean
public void registerMbean() {
if (!mbeanRegistered) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
ObjectName objectName = DruidDataSourceStatManager.addDataSource(DruidDataSource.this,
DruidDataSource.this.name);
DruidDataSource.this.setObjectName(objectName);
DruidDataSource.this.mbeanRegistered = true;
return null;
}
});
}
}
复制代码
通过下面synchronized的注册类,把我们之前启动的mbean和当前init的objectName
注册好,如果没有名称就重新生成了一个单独的id
public synchronized static ObjectName addDataSource(Object dataSource, String name) {
final Map<Object, ObjectName> instances = getInstances();
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
synchronized (instances) {
if (instances.size() == 0) {
try {
ObjectName objectName = new ObjectName(MBEAN_NAME);
if (!mbeanServer.isRegistered(objectName)) {
mbeanServer.registerMBean(instance, objectName);
}
} catch (JMException ex) {
LOG.error("register mbean error", ex);
}
DruidStatService.registerMBean();
}
}
ObjectName objectName = null;
if (name != null) {
try {
objectName = new ObjectName("com.alibaba.druid:type=DruidDataSource,id=" + name);
mbeanServer.registerMBean(dataSource, objectName);
} catch (Throwable ex) {
LOG.error("register mbean error", ex);
objectName = null;
}
}
if (objectName == null) {
try {
int id = System.identityHashCode(dataSource);
objectName = new ObjectName("com.alibaba.druid:type=DruidDataSource,id=" + id);
mbeanServer.registerMBean(dataSource, objectName);
} catch (Throwable ex) {
LOG.error("register mbean error", ex);
objectName = null;
}
}
instances.put(dataSource, objectName);
return objectName;
}
复制代码
等于说每次代码运行init方法时候都会自动注册,而不用像我们示例的代码那样全部都在application中去侵入式的创建mBean了。
总结: 今天主要是了解了druid中对于Mbean的应用,以及Mbean到底是一个什么样的监控工具,自己也实现了一些demo,并且可以借鉴druid中的无侵入式Mbean注册方式,在自己的代码中也这样优雅的实现一些简单的监控。