场景:下班早没事干,技术菜逼只能靠内卷才能在不经意的某个时刻惊艳到别人。。。。。。。最近在写一个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);
}
if (dataSource.getInitialSize() > 0) {
dataSource.getLogWriter();
}
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.学习,各位路过的大佬有什么见解和指导还望不吝赐教,文章如有纰漏,还望指出。
|