本文主要研究一下druid连接池的监控

init

com/alibaba/druid/pool/DruidDataSource.java

public void init() throws SQLException {
    //......
    registerMbean();
    //......
}
DruidDataSource的init方法会执行registerMbean

registerMbean

com/alibaba/druid/pool/DruidDataSource.java

    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;
                }
            });
        }
    }
registerMbean会执行DruidDataSourceStatManager.addDataSource(DruidDataSource.this, DruidDataSource.this.name)

DruidDataSourceStatManager

com/alibaba/druid/stat/DruidDataSourceStatManager.java

    public static synchronized 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;
    }
DruidDataSourceStatManager的addDataSource方法通过mbeanServer.registerMBean(dataSource, objectName)将DruidDataSource注册到mbeanServer中,而DruidDataSource则实现了DruidDataSourceMBean接口

DruidDataSourceMBean

public interface DruidDataSourceMBean extends DruidAbstractDataSourceMBean {
    long getResetCount();

    boolean isEnable();

    String getUrl();

    void shrink();

    int removeAbandoned();

    String dump();

    int getWaitThreadCount();

    int getLockQueueLength();

    long getNotEmptyWaitCount();

    int getNotEmptyWaitThreadCount();

    long getNotEmptySignalCount();

    long getNotEmptyWaitMillis();

    long getNotEmptyWaitNanos();

    void resetStat();

    boolean isResetStatEnable();

    void setResetStatEnable(boolean resetStatEnable);

    String getVersion();

    void setPoolPreparedStatements(boolean poolPreparedStatements);

    int getActivePeak();

    int getPoolingPeak();

    Date getActivePeakTime();

    Date getPoolingPeakTime();

    long getErrorCount();

    ObjectName getObjectName();

    void clearStatementCache() throws SQLException;

    long getDiscardCount();

    void setStatLoggerClassName(String className);

    long getTimeBetweenLogStatsMillis();

    void setTimeBetweenLogStatsMillis(long timeBetweenLogStatsMillis);

    void setConnectionProperties(String connectionProperties);

    int fill() throws SQLException;

    int fill(int toCount) throws SQLException;

    boolean isUseGlobalDataSourceStat();
}
DruidDataSourceMBean按jmx规范命名以MBean结尾,它定义了一系列的getter和操作方法,它还继承了DruidAbstractDataSourceMBean

DruidAbstractDataSourceMBean

com/alibaba/druid/pool/DruidAbstractDataSourceMBean.java

public interface DruidAbstractDataSourceMBean {
    int getLoginTimeout();

    String getDbType();

    String getName();

    int getInitialSize();

    String getUsername();

    String getUrl();

    String getDriverClassName();

    long getConnectCount();

    long getCloseCount();

    long getConnectErrorCount();

    int getPoolingCount();

    long getRecycleCount();

    int getActiveCount();

    long getCreateCount();

    long getDestroyCount();

    long getCreateTimespanMillis();

    long getCommitCount();

    long getRollbackCount();

    long getStartTransactionCount();

    int getQueryTimeout();

    int getTransactionQueryTimeout();

    String getValidationQuery();

    int getValidationQueryTimeout();

    int getMaxWaitThreadCount();

    long getTimeBetweenEvictionRunsMillis();

    long getMinEvictableIdleTimeMillis();

    boolean isRemoveAbandoned();

    long getRemoveAbandonedTimeoutMillis();

    List<String> getActiveConnectionStackTrace();

    List<String> getFilterClassNames();

    boolean isTestOnBorrow();

    void setTestOnBorrow(boolean testOnBorrow);

    boolean isTestOnReturn();

    boolean isTestWhileIdle();

    void setTestWhileIdle(boolean testWhileIdle);

    boolean isDefaultAutoCommit();

    Boolean getDefaultReadOnly();

    Integer getDefaultTransactionIsolation();

    String getDefaultCatalog();

    boolean isPoolPreparedStatements();

    boolean isSharePreparedStatements();

    long getMaxWait();

    int getMinIdle();

    int getMaxIdle();

    long getCreateErrorCount();

    int getMaxActive();

    void setMaxActive(int maxActive);

    long getTimeBetweenConnectErrorMillis();

    int getMaxOpenPreparedStatements();

    long getRemoveAbandonedCount();

    boolean isLogAbandoned();

    void setLogAbandoned(boolean logAbandoned);

    long getDupCloseCount();

    boolean isBreakAfterAcquireFailure();

    int getConnectionErrorRetryAttempts();

    int getMaxPoolPreparedStatementPerConnectionSize();

    void setMaxPoolPreparedStatementPerConnectionSize(int maxPoolPreparedStatementPerConnectionSize);

    String getProperties();

    int getRawDriverMinorVersion();

    int getRawDriverMajorVersion();

    Date getCreatedTime();

    String getValidConnectionCheckerClassName();

    long[] getTransactionHistogramValues();

    void setTransactionThresholdMillis(long transactionThresholdMillis);

    long getTransactionThresholdMillis();

    long getPreparedStatementCount();

    long getClosedPreparedStatementCount();

    long getCachedPreparedStatementCount();

    long getCachedPreparedStatementDeleteCount();

    long getCachedPreparedStatementAccessCount();

    long getCachedPreparedStatementMissCount();

    long getCachedPreparedStatementHitCount();

    boolean isUseOracleImplicitCache();

    void setUseOracleImplicitCache(boolean useOracleImplicitCache);

    int getDriverMajorVersion();

    int getDriverMinorVersion();

    String getExceptionSorterClassName();
}
DruidAbstractDataSourceMBean定义了暴露给jmx的一系列方法

getStatDataForMBean

com/alibaba/druid/pool/DruidDataSource.java

    public Map<String, Object> getStatDataForMBean() {
        try {
            Map<String, Object> map = new HashMap<String, Object>();

            // 0 - 4
            map.put("Name", this.getName());
            map.put("URL", this.getUrl());
            map.put("CreateCount", this.getCreateCount());
            map.put("DestroyCount", this.getDestroyCount());
            map.put("ConnectCount", this.getConnectCount());

            // 5 - 9
            map.put("CloseCount", this.getCloseCount());
            map.put("ActiveCount", this.getActiveCount());
            map.put("PoolingCount", this.getPoolingCount());
            map.put("LockQueueLength", this.getLockQueueLength());
            map.put("WaitThreadCount", this.getNotEmptyWaitThreadCount());

            // 10 - 14
            map.put("InitialSize", this.getInitialSize());
            map.put("MaxActive", this.getMaxActive());
            map.put("MinIdle", this.getMinIdle());
            map.put("PoolPreparedStatements", this.isPoolPreparedStatements());
            map.put("TestOnBorrow", this.isTestOnBorrow());

            // 15 - 19
            map.put("TestOnReturn", this.isTestOnReturn());
            map.put("MinEvictableIdleTimeMillis", this.minEvictableIdleTimeMillis);
            map.put("ConnectErrorCount", this.getConnectErrorCount());
            map.put("CreateTimespanMillis", this.getCreateTimespanMillis());
            map.put("DbType", this.dbTypeName);

            // 20 - 24
            map.put("ValidationQuery", this.getValidationQuery());
            map.put("ValidationQueryTimeout", this.getValidationQueryTimeout());
            map.put("DriverClassName", this.getDriverClassName());
            map.put("Username", this.getUsername());
            map.put("RemoveAbandonedCount", this.getRemoveAbandonedCount());

            // 25 - 29
            map.put("NotEmptyWaitCount", this.getNotEmptyWaitCount());
            map.put("NotEmptyWaitNanos", this.getNotEmptyWaitNanos());
            map.put("ErrorCount", this.getErrorCount());
            map.put("ReusePreparedStatementCount", this.getCachedPreparedStatementHitCount());
            map.put("StartTransactionCount", this.getStartTransactionCount());

            // 30 - 34
            map.put("CommitCount", this.getCommitCount());
            map.put("RollbackCount", this.getRollbackCount());
            map.put("LastError", JMXUtils.getErrorCompositeData(this.getLastError()));
            map.put("LastCreateError", JMXUtils.getErrorCompositeData(this.getLastCreateError()));
            map.put("PreparedStatementCacheDeleteCount", this.getCachedPreparedStatementDeleteCount());

            // 35 - 39
            map.put("PreparedStatementCacheAccessCount", this.getCachedPreparedStatementAccessCount());
            map.put("PreparedStatementCacheMissCount", this.getCachedPreparedStatementMissCount());
            map.put("PreparedStatementCacheHitCount", this.getCachedPreparedStatementHitCount());
            map.put("PreparedStatementCacheCurrentCount", this.getCachedPreparedStatementCount());
            map.put("Version", this.getVersion());

            // 40 -
            map.put("LastErrorTime", this.getLastErrorTime());
            map.put("LastCreateErrorTime", this.getLastCreateErrorTime());
            map.put("CreateErrorCount", this.getCreateErrorCount());
            map.put("DiscardCount", this.getDiscardCount());
            map.put("ExecuteQueryCount", this.getExecuteQueryCount());

            map.put("ExecuteUpdateCount", this.getExecuteUpdateCount());

            return map;
        } catch (JMException ex) {
            throw new IllegalStateException("getStatData error", ex);
        }
    }
DruidDataSource的getStatDataForMBean定义了给jmx的所有监控项

DruidDataSourceUtils

com/alibaba/druid/util/DruidDataSourceUtils.java

    public static Map<String, Object> getStatDataForMBean(Object druidDataSource) {
        if (druidDataSource.getClass() == DruidDataSource.class) {
            return ((DruidDataSource) druidDataSource).getStatDataForMBean();
        }

        try {
            Method method = druidDataSource.getClass().getMethod("getStatDataForMBean");
            Object obj = method.invoke(druidDataSource);
            return (Map<String, Object>) obj;
        } catch (Exception e) {
            LOG.error("getStatDataForMBean error", e);
            return null;
        }
    }
DruidDataSourceUtils提供了静态方法用于获取监控项

小结

DruidDataSource的init方法会执行registerMbean,把自身注册到mbeanServer,它实现了DruidDataSourceMBean接口;而DruidDataSourceUtils提供了静态方法用于获取监控项,它使用的是DruidDataSource的getStatDataForMBean方法(貌似没直接给到jmx),可以利用该方法把指标暴露给micrometer,之后就可以利用micrometer的集成能力输出到各个监控平台。


codecraft
11.9k 声望2k 粉丝

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很自豪告诉世人,我曾经将代码注入生命去打造互联网的浪潮之巅,那是个很疯狂的时代,我在一波波的浪潮上留下...


引用和评论

0 条评论