druid源码分析 (5)

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);
}
复制代码

屏幕快照 2021-11-16 下午9.03.30.png 在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 屏幕快照 2021-11-16 下午9.20.36.png

在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注册方式,在自己的代码中也这样优雅的实现一些简单的监控。

Guess you like

Origin juejin.im/post/7031170088644378655