Druid-代码段-1-4

所属文章:池化技术(一)Druid是如何管理数据库连接的?

 本代码段对应流程1.3,连接可用性测试:


 //数据库连接可用性测试
    protected boolean testConnectionInternal(DruidConnectionHolder holder, Connection conn) {
        String sqlFile = JdbcSqlStat.getContextSqlFile();
        String sqlName = JdbcSqlStat.getContextSqlName();

        if (sqlFile != null) {
            JdbcSqlStat.setContextSqlFile(null);
        }
        if (sqlName != null) {
            JdbcSqlStat.setContextSqlName(null);
        }
        try {
            if (validConnectionChecker != null) { //checker不为空
                //checker是init(主流程2)里通过驱动进行适配的检测者,因为本篇文章基于mysql,所以假设这里适配到的checker是MySqlValidConnectionChecker类型的
                boolean valid = validConnectionChecker.isValidConnection(conn, validationQuery, validationQueryTimeout);
                long currentTimeMillis = System.currentTimeMillis();
                if (holder != null) {
                    holder.lastValidTimeMillis = currentTimeMillis;
                }

                if (valid && isMySql) {
                    //这里在现有驱动版本的情况下拿到的lastPacketReceivedTimeMs始终小于0,因为找不到com.mysql.jdbc.MySQLConnection
                    long lastPacketReceivedTimeMs = MySqlUtils.getLastPacketReceivedTimeMs(conn);
                    if (lastPacketReceivedTimeMs > 0) {
                        long mysqlIdleMillis = currentTimeMillis - lastPacketReceivedTimeMs;
                        if (lastPacketReceivedTimeMs > 0 //
                                && mysqlIdleMillis >= timeBetweenEvictionRunsMillis) {
                            discardConnection(conn);
                            String errorMsg = "discard long time none received connection. "
                                    + ", jdbcUrl : " + jdbcUrl
                                    + ", jdbcUrl : " + jdbcUrl
                                    + ", lastPacketReceivedIdleMillis : " + mysqlIdleMillis;
                            LOG.error(errorMsg);
                            return false;
                        }
                    }
                }

                return valid; //返回验证结果
            }

            if (conn.isClosed()) {
                return false;
            }

            //checker为空时,就直接利用validationQuery进行常规测试
            if (null == validationQuery) {
                return true; //validationQuery为空就单纯返回true
            }

            Statement stmt = null;
            ResultSet rset = null;
            try {
                stmt = conn.createStatement();
                if (getValidationQueryTimeout() > 0) {
                    stmt.setQueryTimeout(validationQueryTimeout);
                }
                rset = stmt.executeQuery(validationQuery);
                if (!rset.next()) {
                    return false; //执行检测语句失败,返回false
                }
            } finally {
                //关闭资源
                JdbcUtils.close(rset);
                JdbcUtils.close(stmt);
            }

            return true; //验证通过返回true
        } catch (Throwable ex) {
            // skip
            return false;
        } finally {
            if (sqlFile != null) {
                JdbcSqlStat.setContextSqlFile(sqlFile);
            }
            if (sqlName != null) {
                JdbcSqlStat.setContextSqlName(sqlName);
            }
        }
    }


    //MySqlValidConnectionChecker类里的验证方法
    public boolean isValidConnection(Connection conn, String validateQuery, int validationQueryTimeout) throws Exception {
        if (conn.isClosed()) {
            return false;
        }

        if (usePingMethod) { //是否启用ping方法(如果驱动程序有该方法,则这里为true,一般情况下都是true)
            if (conn instanceof DruidPooledConnection) {
                conn = ((DruidPooledConnection) conn).getConnection();
            }

            if (conn instanceof ConnectionProxy) {
                conn = ((ConnectionProxy) conn).getRawObject();
            }

            if (clazz.isAssignableFrom(conn.getClass())) {
                if (validationQueryTimeout < 0) {
                    validationQueryTimeout = DEFAULT_VALIDATION_QUERY_TIMEOUT;
                }

                try {
                    //ping对象是初始化时拿到驱动程序的一个Method对象,这里通过invoke触发调用
                    ping.invoke(conn, true, validationQueryTimeout * 1000);
                } catch (InvocationTargetException e) {
                    Throwable cause = e.getCause();
                    if (cause instanceof SQLException) {
                        throw (SQLException) cause;
                    }
                    throw e; //ping出错抛异常
                }
                return true; //通过则返回true
            }
        }

        //如果不支持ping方式检测,则触发SELECT 1的方式进行检测(一般情况下不会触发,都是上面ping方式)
        String query = validateQuery;
        if (validateQuery == null || validateQuery.isEmpty()) {
            query = DEFAULT_VALIDATION_QUERY;
        }

        Statement stmt = null;
        ResultSet rs = null;
        try {
            stmt = conn.createStatement();
            if (validationQueryTimeout > 0) {
                stmt.setQueryTimeout(validationQueryTimeout);
            }
            rs = stmt.executeQuery(query);
            return true;
        } finally {
            JdbcUtils.close(rs);
            JdbcUtils.close(stmt);
        }

    }
posted @ 2019-09-21 12:23  是胖虎捏  阅读(1333)  评论(0编辑  收藏  举报