Druid Issue 可见 Springboot3+nacos2.3.0+druid1.2.22 刷新nacos配置后连接占用问题 · Issue #6004 · alibaba/druid · GitHub
一、环境配置
- JDK17
- druid-spring-boot-3-starter 1.2.23
- spring-cloud-starter-alibaba-nacos-config 2023.0.1.0
- spring-cloud-starter-alibaba-nacos-discovery 2023.0.1.0
- druid 1.2.23
- spring-boot 3.3.0
- spring-cloud 4.1.3
- mariadb-java-client 3.3.3
- nacos-service 2.3.0
二、问题现象
短时间内8次修改nacos service 内配置中心的业务服务配置。执行查询sql时,服务无响应。多次尝试后,该问题为必现问题。
三、问题分析
初步分析:
1、nacos配置多次变更导致该问题,必然与nacos动态刷新配置机制有关。
2、查询sql时无响应,结合8次,再结合druid默认maxActive=8,猜测是数据库连接打满,使得新的查询无法获取有效连接。
前置知识点:
1、nacos配置动态刷新机制
nacos动态更新就用到了RefreshEvent事件,
在NacosContextRefresher#registerNacosListener()里,nacos实现了一个修改配置的回调监听,并且会广播一个RefreshEvent事件,
然后RefreshEventListener会收到事件,并调用ContextRefresher#refresh()->refreshEnvironment(),执行updateEnvironment(),
重新加载Context的Environment,之后继续广播一个EnvironmentChangeEvent事件,ConfigurationPropertiesRebinder接收到该事件后,会调用rebind(),
这里就是更改具体@ConfigurationProperties的配置类了,
具体做法就是在该方法里调用了
this.applicationContext.getAutowireCapableBeanFactory().destroyBean(bean);
this.applicationContext.getAutowireCapableBeanFactory().initializeBean(bean, name);
先destroy原来的bean,然后再重新初始化bean。
2、Spring初始化bean的过程中就包含了一个BeanPostProcessor回调处理,其中实现类ConfigurationPropertiesBindingPostProcessor,就会对@ConfigurationProperties的配置类进行处理,把配置文件内容(比如application.yml,systemProperties等,有优先级顺序,如果是同一个属性还会覆盖)和配置类字段进行bind,bind()->bindObject(),核心在于ConfigurationPropertiesRebinder中的rebind操作。会invoke执行以下代码。
public DruidPooledConnection getConnection() throws SQLException {
return this.getConnection(this.maxWait);
}
重复创建新连接,无法回收旧连接,导致连接池打满,造成服务无响应。
解决方案:
1、设置连接过期时间 ,让连接池能空出来。
2、尽量规避短时间重复频繁更改nacos动态配置。
3、扩大连接池最大连接数。
4、通知第三方组件开发者修复该问题。