[Druid-1.2.11源码系列]-5-DruidDataSource监控信息定期打印日志线程LogStatsThread

5-DruidDataSource监控信息定期打印日志线程LogStatsThread

5.1 简介

前面我们说了初始化《3-Connection连接数据库之前的初始化操作》
初始化线程池时候初始化了一个打印线程信息的线程 LogStatsThread对象logStatsThread创建,调用代码如下所示:
DruidDataSource类型的init方法中调用创建和打印监控信息到日志的线程

  createAndLogThread();

具体逻辑如下:
DruidDataSource类型的createAndLogThread方法

    private void createAndLogThread() {
        if (this.timeBetweenLogStatsMillis <= 0) {
            return;
        }

        String threadName = "Druid-ConnectionPool-Log-" + System.identityHashCode(this);
        logStatsThread = new LogStatsThread(threadName);
        logStatsThread.start();

        this.resetStatEnable = false;
    }

5.2 线程类

5.2.1 线程类LogStatsThread

监控打印到日志的类型LogStatsThread

public class LogStatsThread extends Thread {

        public LogStatsThread(String name){
            super(name);
            this.setDaemon(true);
        }

        public void run() {
            try {
                for (;;) {
                    try {
                        logStats();
                    } catch (Exception e) {
                        LOG.error("logStats error", e);
                    }

                    Thread.sleep(timeBetweenLogStatsMillis);
                }
            } catch (InterruptedException e) {
                // skip
            }
        }
    }

可以看到上面代码使用了循环,然后使用Thread.sleep来进行休眠timeBetweenLogStatsMillis时间,这个代码性能并不好,休眠还占用着线程应该使用wait优化下,调试时候可以指定参数如下参数开启:
下面我配置了10毫秒可以根据实际情况修改

-Ddruid.timeBetweenLogStatsMillis=10

5.2.2 取值然后打印

public void logStats() {
        final DruidDataSourceStatLogger statLogger = this.statLogger;
        if (statLogger == null) {
            return;
        }

        DruidDataSourceStatValue statValue = getStatValueAndReset();

        statLogger.log(statValue);
    }

5.2.3 取值

public DruidDataSourceStatValue getStatValueAndReset() {
        DruidDataSourceStatValue value = new DruidDataSourceStatValue();

        lock.lock();
        try {
         //池内数量
            value.setPoolingCount(this.poolingCount);
            //连接池中连接数的峰值
            value.setPoolingPeak(this.poolingPeak);
            //连接池数目峰值出现的时间
            value.setPoolingPeakTime(this.poolingPeakTime);
			//当前连接池中活跃连接数
            value.setActiveCount(this.activeCount);
            //连接池中活跃连接数峰值
            value.setActivePeak(this.activePeak);
            //活跃连接池峰值出现的时间
            value.setActivePeakTime(this.activePeakTime);
			//连接数
            value.setConnectCount(this.connectCount);
            //关闭的连接数量
            value.setCloseCount(this.closeCount);
            //当前等待获取连接的线程数
            value.setWaitThreadCount(lock.getWaitQueueLength(notEmpty));
            //获取连接时最多等待多少次
            value.setNotEmptyWaitCount(this.notEmptyWaitCount);
            value.setNotEmptyWaitNanos(this.notEmptyWaitNanos);
            //保活连接校验数量
            value.setKeepAliveCheckCount(this.keepAliveCheckCount);

            // reset
            this.poolingPeak = 0;
            this.poolingPeakTime = 0;
            this.activePeak = 0;
            this.activePeakTime = 0;
            this.connectCount = 0;
            this.closeCount = 0;
            this.keepAliveCheckCount = 0;

            this.notEmptyWaitCount = 0;
            this.notEmptyWaitNanos = 0;
        } finally {
            lock.unlock();
        }
		
		//数据源名字
        value.setName(this.getName());
        //数据源类型
        value.setDbType(this.dbTypeName);
        //驱动类
        value.setDriverClassName(this.getDriverClassName());
		//连接的jdbc url
        value.setUrl(this.getUrl());
        //用户名字
        value.setUserName(this.getUsername());
        //过滤器类名字
        value.setFilterClassNames(this.getFilterClassNames());
		
		//初始化连接数量
        value.setInitialSize(this.getInitialSize());
        //最小空闲数量
        value.setMinIdle(this.getMinIdle());
        //最大激活连接数量
        value.setMaxActive(this.getMaxActive());
		//查询超时数
        value.setQueryTimeout(this.getQueryTimeout());
        //   事务查询超时数
        value.setTransactionQueryTimeout(this.getTransactionQueryTimeout());
        value.setLoginTimeout(this.getLoginTimeout());
        //连接合法性检查类
        value.setValidConnectionCheckerClassName(this.getValidConnectionCheckerClassName());
        //异常排序类
        value.setExceptionSorterClassName(this.getExceptionSorterClassName());
//如果为true(默认false),当应用向连接池申请连接时,连接池会判断这条连接是否是可用的。
        value.setTestOnBorrow(this.testOnBorrow);
        //如果为true(默认false),当应用使用完连接,连接池回收连接的时候会判断该连接是否还可用
        value.setTestOnReturn(this.testOnReturn);
        //如果为true(默认true),当应用向连接池申请连接,并且testOnBorrow为false时,连接池将会判断连接是否处于空闲状态,如果是,则验证这条连接是否可用。
        value.setTestWhileIdle(this.testWhileIdle);

        value.setDefaultAutoCommit(this.isDefaultAutoCommit());

        if (defaultReadOnly != null) {
            value.setDefaultReadOnly(defaultReadOnly);
        }
        //事务隔离级别
        value.setDefaultTransactionIsolation(this.getDefaultTransactionIsolation());

        //产生的逻辑连接出错总数
        value.setLogicConnectErrorCount(connectErrorCountUpdater.getAndSet(this, 0));

        //产生的物理连接建立总数
        value.setPhysicalConnectCount(createCountUpdater.getAndSet(this, 0));
        //产生的物理关闭总数
        value.setPhysicalCloseCount(destroyCountUpdater.getAndSet(this, 0));
        //产生的物理连接失败总数
        value.setPhysicalConnectErrorCount(createErrorCountUpdater.getAndSet(this, 0));
//执行数
        value.setExecuteCount(this.getAndResetExecuteCount());
        //错误数
        value.setErrorCount(errorCountUpdater.getAndSet(this, 0));
        //提交数
        value.setCommitCount(commitCountUpdater.getAndSet(this, 0));
        ///回滚数
        value.setRollbackCount(rollbackCountUpdater.getAndSet(this, 0));

        //PSCache命中次数
        value.setPstmtCacheHitCount(cachedPreparedStatementHitCountUpdater.getAndSet(this,0));
        //PSCache未命中次数
        value.setPstmtCacheMissCount(cachedPreparedStatementMissCountUpdater.getAndSet(this, 0));

        value.setStartTransactionCount(startTransactionCountUpdater.getAndSet(this, 0));
        //事务运行时间分布,分布区间为[0-10 ms, 10-100 ms, 100-1 s, 1-10 s, 10-100 s, >100 s],这个值是一个数组,数值的索引位的含义如上述,第几索引上的数据就代表在这个时间区间内包含的事务数
        value.setTransactionHistogram(this.getTransactionHistogram().toArrayAndReset());

        //连接耗时分布
        value.setConnectionHoldTimeHistogram(this.getDataSourceStat().getConnectionHoldHistogram().toArrayAndReset());
        value.setRemoveAbandoned(this.isRemoveAbandoned());
        //Clob打开数
        value.setClobOpenCount(this.getDataSourceStat().getClobOpenCountAndReset());
        //Blob打开数
        value.setBlobOpenCount(this.getDataSourceStat().getBlobOpenCountAndReset());

        value.setSqlSkipCount(this.getDataSourceStat().getSkipSqlCountAndReset());
        value.setSqlList(this.getDataSourceStat().getSqlStatMapAndReset());

        return value;
    }

5.2.4 打印

DruidDataSourceStatLoggerImpl类型的log方法

 @Override
    public void log(DruidDataSourceStatValue statValue) {
    //日志开关是否开启
        if (!isLogEnable()) {
            return;
        }
        //统计数据转map类型
        Map<String, Object> map = new LinkedHashMap<String, Object>();

        map.put("url", statValue.url);
        map.put("dbType", statValue.getDbType());
        map.put("name", statValue.getName());
        map.put("activeCount", statValue.getActiveCount());

        if (statValue.getActivePeak() > 0) {
            map.put("activePeak", statValue.getActivePeak());
            map.put("activePeakTime", statValue.getActivePeakTime());
        }
        map.put("poolingCount", statValue.getPoolingCount());
        if (statValue.getPoolingPeak() > 0) {
            map.put("poolingPeak", statValue.getPoolingPeak());
            map.put("poolingPeakTime", statValue.getPoolingPeakTime());
        }
        map.put("connectCount", statValue.getConnectCount());
        map.put("closeCount", statValue.getCloseCount());

        if (statValue.getWaitThreadCount() > 0) {
            map.put("waitThreadCount", statValue.getWaitThreadCount());
        }

        if (statValue.getNotEmptyWaitCount() > 0) {
            map.put("notEmptyWaitCount", statValue.getNotEmptyWaitCount());
        }

        if (statValue.getNotEmptyWaitMillis() > 0) {
            map.put("notEmptyWaitMillis", statValue.getNotEmptyWaitMillis());
        }

        if (statValue.getLogicConnectErrorCount() > 0) {
            map.put("logicConnectErrorCount", statValue.getLogicConnectErrorCount());
        }

        if (statValue.getPhysicalConnectCount() > 0) {
            map.put("physicalConnectCount", statValue.getPhysicalConnectCount());
        }

        if (statValue.getPhysicalCloseCount() > 0) {
            map.put("physicalCloseCount", statValue.getPhysicalCloseCount());
        }

        if (statValue.getPhysicalConnectErrorCount() > 0) {
            map.put("physicalConnectErrorCount", statValue.getPhysicalConnectErrorCount());
        }

        if (statValue.getExecuteCount() > 0) {
            map.put("executeCount", statValue.getExecuteCount());
        }

        if (statValue.getErrorCount() > 0) {
            map.put("errorCount", statValue.getErrorCount());
        }

        if (statValue.getCommitCount() > 0) {
            map.put("commitCount", statValue.getCommitCount());
        }

        if (statValue.getRollbackCount() > 0) {
            map.put("rollbackCount", statValue.getRollbackCount());
        }

        if (statValue.getPstmtCacheHitCount() > 0) {
            map.put("pstmtCacheHitCount", statValue.getPstmtCacheHitCount());
        }

        if (statValue.getPstmtCacheMissCount() > 0) {
            map.put("pstmtCacheMissCount", statValue.getPstmtCacheMissCount());
        }

        if (statValue.getStartTransactionCount() > 0) {
            map.put("startTransactionCount", statValue.getStartTransactionCount());
            map.put("transactionHistogram", rtrim(statValue.getTransactionHistogram()));
        }

        if (statValue.getConnectCount() > 0) {
            map.put("connectionHoldTimeHistogram", rtrim(statValue.getConnectionHoldTimeHistogram()));
        }

        if (statValue.getClobOpenCount() > 0) {
            map.put("clobOpenCount", statValue.getClobOpenCount());
        }

        if (statValue.getBlobOpenCount() > 0) {
            map.put("blobOpenCount", statValue.getBlobOpenCount());
        }

        if (statValue.getSqlSkipCount() > 0) {
            map.put("sqlSkipCount", statValue.getSqlSkipCount());
        }

        ArrayList<Map<String, Object>> sqlList = new ArrayList<Map<String, Object>>();
        if (statValue.sqlList.size() > 0) {
            for (JdbcSqlStatValue sqlStat : statValue.getSqlList()) {
                Map<String, Object> sqlStatMap = new LinkedHashMap<String, Object>();
                sqlStatMap.put("sql", sqlStat.getSql());

                if (sqlStat.getExecuteCount() > 0) {
                    sqlStatMap.put("executeCount", sqlStat.getExecuteCount());
                    sqlStatMap.put("executeMillisMax", sqlStat.getExecuteMillisMax());
                    sqlStatMap.put("executeMillisTotal", sqlStat.getExecuteMillisTotal());

                    sqlStatMap.put("executeHistogram", rtrim(sqlStat.getExecuteHistogram()));
                    sqlStatMap.put("executeAndResultHoldHistogram", rtrim(sqlStat.getExecuteAndResultHoldHistogram()));
                }

                long executeErrorCount = sqlStat.getExecuteErrorCount();
                if (executeErrorCount > 0) {
                    sqlStatMap.put("executeErrorCount", executeErrorCount);
                }

                int runningCount = sqlStat.getRunningCount();
                if (runningCount > 0) {
                    sqlStatMap.put("runningCount", runningCount);
                }

                int concurrentMax = sqlStat.getConcurrentMax();
                if (concurrentMax > 0) {
                    sqlStatMap.put("concurrentMax", concurrentMax);
                }

                if (sqlStat.getFetchRowCount() > 0) {
                    sqlStatMap.put("fetchRowCount", sqlStat.getFetchRowCount());
                    sqlStatMap.put("fetchRowCountMax", sqlStat.getFetchRowCountMax());
                    sqlStatMap.put("fetchRowHistogram", rtrim(sqlStat.getFetchRowHistogram()));
                }

                if (sqlStat.getUpdateCount() > 0) {
                    sqlStatMap.put("updateCount", sqlStat.getUpdateCount());
                    sqlStatMap.put("updateCountMax", sqlStat.getUpdateCountMax());
                    sqlStatMap.put("updateHistogram", rtrim(sqlStat.getUpdateHistogram()));
                }

                if (sqlStat.getInTransactionCount() > 0) {
                    sqlStatMap.put("inTransactionCount", sqlStat.getInTransactionCount());
                }

                if (sqlStat.getClobOpenCount() > 0) {
                    sqlStatMap.put("clobOpenCount", sqlStat.getClobOpenCount());
                }

                if (sqlStat.getBlobOpenCount() > 0) {
                    sqlStatMap.put("blobOpenCount", sqlStat.getBlobOpenCount());
                }

                sqlList.add(sqlStatMap);
            }

            map.put("sqlList", sqlList);
        }

        if (statValue.getKeepAliveCheckCount() > 0) {
            map.put("keepAliveCheckCount", statValue.getKeepAliveCheckCount());
        }

        String text = JSONUtils.toJSONString(map);

        log(text);
    }

可以看到监控数据最终转换了为了JSON格式打印到了控制台

5.2.5 打印的样例数据如下

[16/06/22 22:29:08:033 CST] Druid-ConnectionPool-Log-1636506029  INFO pool.DruidDataSourceStatLoggerImpl: {"url":"jdbc:mysql://localhost:3306/druid?allowMultiQueries=true","dbType":"mysql","name":"DataSource-1636506029","activeCount":1,"poolingCount":0,"connectCount":0,"closeCount":0,"executeCount":1}

格式化下json内容如下所示:

{
    "url": "jdbc:mysql://localhost:3306/druid?allowMultiQueries=true", 
    "dbType": "mysql", 
    "name": "DataSource-1636506029", 
    "activeCount": 1, 
    "poolingCount": 0, 
    "connectCount": 0, 
    "closeCount": 0, 
    "executeCount": 1
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宋小生的博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值