dbcp2配置参数不生效导致的请求阻塞

场景:下班早没事干,技术菜逼只能靠内卷才能在不经意的某个时刻惊艳到别人。。。。。。。最近在写一个java代码生成器,由于要支持多种数据库类型,所以数据库连接是在代码里面通过硬编码的方式初始化的,选择了dbcp2作为数据库连接池。
一切准备就绪,测试的时候发现一个奇妙的现象,每次去查询表结构信息的时候,重新查询每到第九次,请求就挂起了,然后前端请求因为超时就嗝屁了。如下图(图1):
在这里插入图片描述
1.贴上初始化数据库的代码,如下图(图2):

public static DataSource getDataSource(GeneratorConfig generatorConfig) {
        String jdbcUrl = generatorConfig.getJdbcUrl() + ":" + generatorConfig.getUsername(); ;
        DataSource dataSource = DATA_SOURCE_MAP.computeIfAbsent(jdbcUrl, key -> {
            Properties properties = new Properties();
            properties.put("driverClassName", generatorConfig.getDriverClass());
            properties.put("url", generatorConfig.getJdbcUrl());
            properties.put("username", generatorConfig.getUsername());
            properties.put("password", generatorConfig.getPassword());
            // 初始连接数
            properties.put("initialSize", 1);
            // 最大活跃数
            properties.put("maxTotal", 30);
            properties.put("minIdle", 5);
            properties.put("maxIdle", 10);
            // 最长等待时间(毫秒)
            properties.put("maxWaitMillis", 1000);
            // 程序中的连接不使用后是否被连接池回收
            properties.put("removeAbandonedOnMaintenance", true);
            properties.put("removeAbandonedOnBorrow", true);
            // 连接在所指定的秒数内未使用才会被删除(秒)
            properties.put("removeAbandonedTimeout", 5);
            try {
                return BasicDataSourceFactory.createDataSource(properties);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        });
        if (dataSource == null) {
            throw new RuntimeException("连接数据库失败");
        }
        return dataSource;
    }

看看配置,貌似也没有什么毛病,但是请求第九次时咋就挂起了呢?
2.本着内卷的目的,吃了一个苹果沉下心来,开始debug。
在这里插入图片描述
老铁们。上图(图3)是BasicDataSourceFactory.createDataSource(properties);执行这个创建数据源的方法里面打印的变量信息,右边的小图是我传进去的参数,左边的图是初始化后的数据源相关参数,大家仔细看,除了红色箭头标记的几个参数可以对的上,绿的标记的几个参数压根用的是默认值,不是我传进去的参数,,,,,啊,妈妈,,闹鬼了/再吃一包辣条压压惊,,,咱们继续往下看。。。

public static BasicDataSource createDataSource(final Properties properties) throws Exception {
    final BasicDataSource dataSource = new BasicDataSource();
    String value = null;

    value = properties.getProperty(PROP_DEFAULT_AUTO_COMMIT);
    if (value != null) {
        dataSource.setDefaultAutoCommit(Boolean.valueOf(value));
    }

    value = properties.getProperty(PROP_DEFAULT_READ_ONLY);
    if (value != null) {
        dataSource.setDefaultReadOnly(Boolean.valueOf(value));
    }

    value = properties.getProperty(PROP_DEFAULT_TRANSACTION_ISOLATION);
    if (value != null) {
        int level = PoolableConnectionFactory.UNKNOWN_TRANSACTION_ISOLATION;
        if ("NONE".equalsIgnoreCase(value)) {
            level = Connection.TRANSACTION_NONE;
        } else if ("READ_COMMITTED".equalsIgnoreCase(value)) {
            level = Connection.TRANSACTION_READ_COMMITTED;
        } else if ("READ_UNCOMMITTED".equalsIgnoreCase(value)) {
            level = Connection.TRANSACTION_READ_UNCOMMITTED;
        } else if ("REPEATABLE_READ".equalsIgnoreCase(value)) {
            level = Connection.TRANSACTION_REPEATABLE_READ;
        } else if ("SERIALIZABLE".equalsIgnoreCase(value)) {
            level = Connection.TRANSACTION_SERIALIZABLE;
        } else {
            try {
                level = Integer.parseInt(value);
            } catch (final NumberFormatException e) {
                System.err.println("Could not parse defaultTransactionIsolation: " + value);
                System.err.println("WARNING: defaultTransactionIsolation not set");
                System.err.println("using default value of database driver");
                level = PoolableConnectionFactory.UNKNOWN_TRANSACTION_ISOLATION;
            }
        }
        dataSource.setDefaultTransactionIsolation(level);
    }

    value = properties.getProperty(PROP_DEFAULT_CATALOG);
    if (value != null) {
        dataSource.setDefaultCatalog(value);
    }

    value = properties.getProperty(PROP_DEFAULT_SCHEMA);
    if (value != null) {
        dataSource.setDefaultSchema(value);
    }

    value = properties.getProperty(PROP_CACHE_STATE);
    if (value != null) {
        dataSource.setCacheState(Boolean.valueOf(value).booleanValue());
    }

    value = properties.getProperty(PROP_DRIVER_CLASS_NAME);
    if (value != null) {
        dataSource.setDriverClassName(value);
    }

    value = properties.getProperty(PROP_LIFO);
    if (value != null) {
        dataSource.setLifo(Boolean.valueOf(value).booleanValue());
    }

    value = properties.getProperty(PROP_MAX_TOTAL);
    if (value != null) {
        dataSource.setMaxTotal(Integer.parseInt(value));
    }

    value = properties.getProperty(PROP_MAX_IDLE);
    if (value != null) {
        dataSource.setMaxIdle(Integer.parseInt(value));
    }

    value = properties.getProperty(PROP_MIN_IDLE);
    if (value != null) {
        dataSource.setMinIdle(Integer.parseInt(value));
    }

    value = properties.getProperty(PROP_INITIAL_SIZE);
    if (value != null) {
        dataSource.setInitialSize(Integer.parseInt(value));
    }

    value = properties.getProperty(PROP_MAX_WAIT_MILLIS);
    if (value != null) {
        dataSource.setMaxWaitMillis(Long.parseLong(value));
    }

    value = properties.getProperty(PROP_TEST_ON_CREATE);
    if (value != null) {
        dataSource.setTestOnCreate(Boolean.valueOf(value).booleanValue());
    }

    value = properties.getProperty(PROP_TEST_ON_BORROW);
    if (value != null) {
        dataSource.setTestOnBorrow(Boolean.valueOf(value).booleanValue());
    }

    value = properties.getProperty(PROP_TEST_ON_RETURN);
    if (value != null) {
        dataSource.setTestOnReturn(Boolean.valueOf(value).booleanValue());
    }

    value = properties.getProperty(PROP_TIME_BETWEEN_EVICTION_RUNS_MILLIS);
    if (value != null) {
        dataSource.setTimeBetweenEvictionRunsMillis(Long.parseLong(value));
    }

    value = properties.getProperty(PROP_NUM_TESTS_PER_EVICTION_RUN);
    if (value != null) {
        dataSource.setNumTestsPerEvictionRun(Integer.parseInt(value));
    }

    value = properties.getProperty(PROP_MIN_EVICTABLE_IDLE_TIME_MILLIS);
    if (value != null) {
        dataSource.setMinEvictableIdleTimeMillis(Long.parseLong(value));
    }

    value = properties.getProperty(PROP_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS);
    if (value != null) {
        dataSource.setSoftMinEvictableIdleTimeMillis(Long.parseLong(value));
    }

    value = properties.getProperty(PROP_EVICTION_POLICY_CLASS_NAME);
    if (value != null) {
        dataSource.setEvictionPolicyClassName(value);
    }

    value = properties.getProperty(PROP_TEST_WHILE_IDLE);
    if (value != null) {
        dataSource.setTestWhileIdle(Boolean.valueOf(value).booleanValue());
    }

    value = properties.getProperty(PROP_PASSWORD);
    if (value != null) {
        dataSource.setPassword(value);
    }

    value = properties.getProperty(PROP_URL);
    if (value != null) {
        dataSource.setUrl(value);
    }

    value = properties.getProperty(PROP_USER_NAME);
    if (value != null) {
        dataSource.setUsername(value);
    }

    value = properties.getProperty(PROP_VALIDATION_QUERY);
    if (value != null) {
        dataSource.setValidationQuery(value);
    }

    value = properties.getProperty(PROP_VALIDATION_QUERY_TIMEOUT);
    if (value != null) {
        dataSource.setValidationQueryTimeout(Integer.parseInt(value));
    }

    value = properties.getProperty(PROP_ACCESS_TO_UNDERLYING_CONNECTION_ALLOWED);
    if (value != null) {
        dataSource.setAccessToUnderlyingConnectionAllowed(Boolean.valueOf(value).booleanValue());
    }

    value = properties.getProperty(PROP_REMOVE_ABANDONED_ON_BORROW);
    if (value != null) {
        dataSource.setRemoveAbandonedOnBorrow(Boolean.valueOf(value).booleanValue());
    }

    value = properties.getProperty(PROP_REMOVE_ABANDONED_ON_MAINTENANCE);
    if (value != null) {
        dataSource.setRemoveAbandonedOnMaintenance(Boolean.valueOf(value).booleanValue());
    }

    value = properties.getProperty(PROP_REMOVE_ABANDONED_TIMEOUT);
    if (value != null) {
        dataSource.setRemoveAbandonedTimeout(Integer.parseInt(value));
    }

    value = properties.getProperty(PROP_LOG_ABANDONED);
    if (value != null) {
        dataSource.setLogAbandoned(Boolean.valueOf(value).booleanValue());
    }

    value = properties.getProperty(PROP_ABANDONED_USAGE_TRACKING);
    if (value != null) {
        dataSource.setAbandonedUsageTracking(Boolean.valueOf(value).booleanValue());
    }

    value = properties.getProperty(PROP_POOL_PREPARED_STATEMENTS);
    if (value != null) {
        dataSource.setPoolPreparedStatements(Boolean.valueOf(value).booleanValue());
    }

    value = properties.getProperty(PROP_MAX_OPEN_PREPARED_STATEMENTS);
    if (value != null) {
        dataSource.setMaxOpenPreparedStatements(Integer.parseInt(value));
    }

    value = properties.getProperty(PROP_CONNECTION_INIT_SQLS);
    if (value != null) {
        dataSource.setConnectionInitSqls(parseList(value, ';'));
    }

    value = properties.getProperty(PROP_CONNECTION_PROPERTIES);
    if (value != null) {
        final Properties p = getProperties(value);
        final Enumeration<?> e = p.propertyNames();
        while (e.hasMoreElements()) {
            final String propertyName = (String) e.nextElement();
            dataSource.addConnectionProperty(propertyName, p.getProperty(propertyName));
        }
    }

    value = properties.getProperty(PROP_MAX_CONN_LIFETIME_MILLIS);
    if (value != null) {
        dataSource.setMaxConnLifetimeMillis(Long.parseLong(value));
    }

    value = properties.getProperty(PROP_LOG_EXPIRED_CONNECTIONS);
    if (value != null) {
        dataSource.setLogExpiredConnections(Boolean.valueOf(value).booleanValue());
    }

    value = properties.getProperty(PROP_JMX_NAME);
    if (value != null) {
        dataSource.setJmxName(value);
    }

    value = properties.getProperty(PROP_ENABLE_AUTO_COMMIT_ON_RETURN);
    if (value != null) {
        dataSource.setAutoCommitOnReturn(Boolean.valueOf(value).booleanValue());
    }

    value = properties.getProperty(PROP_ROLLBACK_ON_RETURN);
    if (value != null) {
        dataSource.setRollbackOnReturn(Boolean.valueOf(value).booleanValue());
    }

    value = properties.getProperty(PROP_DEFAULT_QUERY_TIMEOUT);
    if (value != null) {
        dataSource.setDefaultQueryTimeout(Integer.valueOf(value));
    }

    value = properties.getProperty(PROP_FAST_FAIL_VALIDATION);
    if (value != null) {
        dataSource.setFastFailValidation(Boolean.valueOf(value).booleanValue());
    }

    value = properties.getProperty(PROP_DISCONNECTION_SQL_CODES);
    if (value != null) {
        dataSource.setDisconnectionSqlCodes(parseList(value, ','));
    }

    value = properties.getProperty(PROP_CONNECTION_FACTORY_CLASS_NAME);
    if (value != null) {
        dataSource.setConnectionFactoryClassName(value);
    }

    // DBCP-215
    // Trick to make sure that initialSize connections are created
    if (dataSource.getInitialSize() > 0) {
        dataSource.getLogWriter();
    }

    // Return the configured DataSource instance
    return dataSource;
}

如上图,dbcp2中是通过value = properties.getProperty(key);这个方法读取的我们put到Properties中的参数,具体代码如下图:

public String getProperty(String key) {
    Object oval = super.get(key);
    String sval = (oval instanceof String) ? (String)oval : null;
    return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval;
}

纳尼,如果是oval不是String类型那么给一个null,也就是说我们put进去Properties中的值如果不是String,他就默认给了一个null,所以我设置的参数中下图(图4)中红框中的就没有起作用。在这里插入图片描述
正因为如此,这些参数使用的都是默认的参数,如上面提到的图3。默认的最大活跃连接数是8,8次请求后没有可用的连接,数据库连接池资源耗尽,又因为默认的maxWaitMillis=-1,所以无限期等待下去,后续请求都阻塞了,导致请求挂起。对于这块关于数据库配置造成的问题大家可以自行搜索了解更多,,码字太慢了,,需要解析的内容比较多。
3.解决:将Properties中put的参数值都转换为String类型即可。如下图(图5):
在这里插入图片描述
4.附上从Properties中通过getProperty(key)读取参数值的小测试:
在这里插入图片描述
5.总结,总的来说就是因为Properties中的参数值只支持String类型的。其他类型的参数值获取不到,导致数据库初始化配置用的默认配置不合理,导致连接不能及时归还,连接池资源耗尽,请求阻塞。
6.学习,各位路过的大佬有什么见解和指导还望不吝赐教,文章如有纰漏,还望指出。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值