问题描述
如题所述,我们每新增一个数据源并且往 etcd 去生产的时候,etcd 消费者收到监听到的内容去消费,产生大量的数据库连接(通过 SHOW PROCESSLIST 查到)
原因分析
故事是因为项目本身有一个多租户功能,每个租户独立动态生成一个数据库,那么这里就会牵扯到动态数据源的话题(配置最小连接数是 10)
TenantInfo tenantInfo = new Gson().fromJson(multiTenantStr, TenantInfo.class);
log.info(tenantInfo.toString());
HikariDataSource dataSource = new HikariDataSource(hikariConfig);
dataSource.setDriverClassName(tenantInfo.getDatasourceDriver());
dataSource.setJdbcUrl(tenantInfo.getDatasourceUrl());
dataSource.setUsername(tenantInfo.getDatasourceUsername());
dataSource.setPassword(tenantInfo.getDatasourcePassword());
handleHikariConfig(hikariConfig, dataSource);
一开始有 2 个很奇怪的现象,下面会一一解释
1、Etcd 明明只生产一次,为什么会反复的收到很多条一模一样的消息?
经排查发现,是因为 Etcd 报错导致反复重复的消费本条消息,但是呢?我们没在后台看到报错日志是因为异步监听,所以后来我们也给它加了 try...catch... 来捕获异常,发现异常来自于以下第 2 点会说明
2、理论上是新生产的连接池应该是新租户对应的数据库信息,为什么老是生产默认的数据库连接池?
HikariDataSource dataSource = new HikariDataSource(hikariConfig);
dataSource.setDriverClassName(tenantInfo.getDatasourceDriver());
首先,hikariConfig 因为拿的是默认的 application.yml 配置的连接池配置信息,拿肯定里面包含了默认的 JDBC-URL,然后一旦 new 成功后,就会自动连接到数据库,新增至少 10 条连接数。紧接着,在 setXxx 属性的时候,因为这个是连接好了的连接池信息,这逻辑本身这么顺序颠倒着写就是不对的。虽然 HikariPoolMXBean 是支持动态修改配置信息,但也是部分属性支持修改,其余一旦动了就会抛异常,抛异常,抛异常……那么再感谢第 1 点说的 Etcd 报错没有 ACK 的问题引起的重试机制,完美配合下,你说这个数据库你不爆谁爆?!
其实,主要就是以上两点加起来,导致把数据库连接数干爆,按一个最小 10 来算的话,Etcd 如果没成功 ACK 的话,就会启动重试机制,重试 N 次,那就会 N*10 条数据库连接数,这能不打爆吗?!
解决方案
HikariConfig config = buildConfig(hikariConfig, tenantInfo);
HikariDataSource dataSource = new HikariDataSource(config);
那通过我们的分析就很好解决这个问题,毕竟知道问题产生的缘由。
首先将默认配置拿到,然后通过新租户对应的数据库配置信息,来覆盖默认的配置信息。完事后再去生成新的 HikariDataSource 即可,完美解决~
温馨提示
new HikariDataSource(); // 无参构造: 使用懒加载模式,第一次调用的时候才会连接数据库new HikariDataSource(config); // 有参构造:在 new 的时候,就会连接到数据库