聊聊druid连接池的监控
序
本文主要研究一下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>() {@Overridepublic 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 - 4map.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 - 9map.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 - 14map.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 - 19map.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 - 24map.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 - 29map.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 - 34map.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 - 39map.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的集成能力输出到各个监控平台。