atomikos数据源连接超时和耗尽问题
问题描述项目里之前用的是原生druid数据源,没有任何问题。后来使用了AtomikosDataSourceBean,其中用的DruidXADataSource数据源,java配置如下@Bean(name = "bdpDatasource")public DataSource bdpDatasource() {DruidXADataSource druidXADataSource = new Dru
1.问题描述
项目里之前用的是原生druid数据源,后来为了解决多个数据源的事务一致性问题,引入了atomikos数据源。
java配置如下
@Bean(name = "bdpDatasource")
public DataSource bdpDatasource() {
DruidXADataSource druidXADataSource = new DruidXADataSource();
druidXADataSource.setUrl(bdpProperties.getUrl());
druidXADataSource.setPassword(bdpProperties.getPassword());
druidXADataSource.setUsername(bdpProperties.getUsername());
druidXADataSource.setConnectionProperties(bdpProperties.getConnectionProperties());
druidXADataSource.setDriverClassName(bdpProperties.getDriverClassName());
druidXADataSource.setMaxActive(bdpProperties.getMaxActive());
druidXADataSource.setMaxWait(bdpProperties.getMaxWait());
druidXADataSource.setMinIdle(bdpProperties.getMinIdle());
druidXADataSource.setInitialSize(bdpProperties.getInitialSize());
druidXADataSource.setValidationQuery(bdpProperties.getValidationQuery());
druidXADataSource.setValidationQueryTimeout(bdpProperties.getValidationQueryTimeout());
druidXADataSource.setTestOnBorrow(bdpProperties.getTestOnBorrow());
druidXADataSource.setTestOnReturn(bdpProperties.getTestOnReturn());
druidXADataSource.setTestWhileIdle(bdpProperties.getTestWhileIdle());
druidXADataSource.setTimeBetweenEvictionRunsMillis(bdpProperties.getTimeBetweenEvictionRunsMillis());
druidXADataSource.setMinEvictableIdleTimeMillis(bdpProperties.getMinEvictableIdleTimeMillis());
try {
druidXADataSource.setFilters(bdpProperties.getFilters());
} catch (SQLException e) {
e.printStackTrace();
}
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(druidXADataSource);
xaDataSource.setMaxPoolSize(bdpProperties.getMaxActive());
xaDataSource.setMinPoolSize(bdpProperties.getMinIdle());
xaDataSource.setMaxLifetime(bdpProperties.getMaxWait());
xaDataSource.setTestQuery(bdpProperties.getValidationQuery());
xaDataSource.setUniqueResourceName("bdpDatasource");
return xaDataSource;
}
properties配置如下
spring.datasource.druid.driverClassName=com.mysql.jdbc.Driver
spring.datasource.druid.maxActive=20
spring.datasource.druid.maxWait=20000
spring.datasource.druid.minIdle=5
spring.datasource.druid.initialSize=5
spring.datasource.druid.validationQuery=select 1
spring.datasource.druid.validationQueryTimeout=30000
spring.datasource.druid.testOnBorrow=true
spring.datasource.druid.testOnReturn=false
spring.datasource.druid.testWhileIdle=false
spring.datasource.druid.timeBetweenEvictionRunsMillis=60000
spring.datasource.druid.minEvictableIdleTimeMillis=300000
#spring.datasource.druid.max-evictable-idle-time-millis=900000
spring.datasource.druid.filters=stat,wall,slf4j,config
结果开发环境偶尔报如下错误
上线到生产环境后报如下错误
2.问题重现
开始看生产环境的报错日志,怀疑是数据库那边的问题,因为测试环境从没有报过生产环境这样的错误,所以排除代码原因。
后来同事反馈,可能是测试和生产并发量不一样,因为生产的并发量多,其中有个接口是隔20秒就会调一次的,于是造成连接耗尽。
于是开始使用jemter压测,测试的环境如下
- 使用现有atomikos数据源配置
1.使用jmeter模拟2000个并发调用任意一个接口,结果重现了生产环境的错误。
2.将数据源配置的maxActive参数改为100,再使用jmeter模拟2000个并发调用接口,结果没有任何问题
- 使用以前的druid数据源配置
maxActive改回20,再使用jmeter模拟2000个并发调用接口,结果没有任何问题
3.分析推测
看来是连接数太小,导致atomikos连接耗尽。
但是之前咱们用的是原生druid数据源,maxActive配置也只有20,为什么没有耗尽呢?
查看如下druid的数据源监控界面
发现在maxActive相同的前提下,当使用原生druid时
活跃连接数在并发操作结束后,很快恢复到最小连接数左右
当使用atomikos时
活跃连接数在并发操作结束后,需要等一段时间才能恢复到最小连接数左右
4.结论
DruidXADataSource的连接归还速度远小于原生druid,导致并发量大的情况下,由于本来占用连接就多,归还速度又慢,最终出现上述问题。
5.解决
调大maxActive为100
6.参考资料
https://blog.csdn.net/taxuexunmei414923794/article/details/14450181
7.后续方案
后来发现这种方式不够彻底,因为当并发量继续增加时,maxActive参数不得不继续调大,否则还是可能出现上述问题。
最终,尝试将DruidXADataSource 换成MysqXADataSource ,在maxActive依旧是20的情况下,经过压力测试,发现问题得以彻底解决。
@Bean(name = "bdpDatasource")
public DataSource bdpDatasource() {
MysqlXADataSource mysqlXADataSource = new MysqlXADataSource();
mysqlXADataSource.setUrl(bdpProperties.getUrl());
mysqlXADataSource.setPinGlobalTxToPhysicalConnection(true);
mysqlXADataSource.setUser(bdpProperties.getUsername());
Properties properties = druidDatasource.getConnectProperties();
PasswordDecryptUtil config = new PasswordDecryptUtil();
mysqlXADataSource.setPassword(config.decryptPassword(properties, bdpProperties.getPassword()));
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(mysqlXADataSource);
xaDataSource.setMaxPoolSize(bdpProperties.getMaxActive());
xaDataSource.setMinPoolSize(bdpProperties.getMinIdle());
xaDataSource.setMaxLifetime(bdpProperties.getMaxWait());
xaDataSource.setTestQuery(bdpProperties.getValidationQuery());
xaDataSource.setUniqueResourceName("bdpDatasource");
return xaDataSource;
}
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)