数据库连接池提前初始化引发的异常

背景

我们线上一个项目在发版本时,经常会有部分实例无法正常启动的情况,特别是在没有进行灰度发布的场景下,基本上很难正常的启动成功,异常信息如下:

问题分析

看日志可以很容易看出来,启动异常的原因是hikari的配置绑定异常,原因是hikari的连接池已经seal了:The configuration of the pool is sealed once started.

字面意思:连接池配置一旦启动就被密封。那么问题来了,这个状态是怎么设置的?我的服务明明还没有启动;就算设置了,理论上配置初始化应该只会初始经一次,为什么会报错?

sealed状态设置

我们先看一下hikari的源码,找一下连接池的sealed状态是怎么设置的:

在HikariConfig中查看设置sealed字段的方法,查看它的调用链路,发现只有两个地方调用了此方法来设置sealed状态为true,一个是构造函数,也就是初始化数据源时:

另一个是获取连接时:

可以看到,在HikariConfig构造函数中,会先初始化连接池,然后设置sealed状态。在getConnection时,会判断连接池是不是已经被初始化了,如果没有,则会初始化连接池,并设置sealed状态。

异常来源

我们现在知道sealed状态的设置时机,那么The configuration of the pool is sealed once started. 这个异常又是什么时候被抛出的呢,同样是在HikariConfig中搜索对应异常,可以看到这个异常都是通过checkIfSealed方法抛出,很明显,这个方法就是检查sealed状态的,查看它的调用链路:

可以看到,在设置HikariConfig的任意属性时,都会先检查状态。这么看来,sealed其实就是为了在连接池初始化完成之后,不允许再动态的去更新配置(可以使用其它的方式),那么理论上来说,Spring容器还在启动过程中, 为什么在设置属性之前,sealed状态就已经被设置的呢?

异常原因

我们回过去再去看一下启动日志,通过上面的源码,我们可以看到在初始化连接池的前后,都会打印INFO日志,那么我们可以搜索对应的日志查看连接池的初始化情况:

从日志中可以看到,我们前面两个数据源都正常初始化完成了(主从数据库),但是后面马上又报错了(Spring绑定属性),好像看不出什么来,但是如果我们关注一下日志打印对应的线程就可以发现,初始化连接池和报错的线程并不是同一个!前面的初始化日志,都是在dubbo线程中打印的,而后面报错的日志,是在main线程中。

这时候就很明显了,上面说到,sealed状态不仅在构造函数中设置,在getConnection时也会去初始化连接池并更新状态。这明显就是spring容器还在初始化中,但是dubbo服务已经提前暴露了,导致有请求进来开始请求DB了。

异常流程

  1. dubbo服务成功暴露
  2. dubbo请求进入,存在DB请求,调用getConnection初始化连接时,sealed = true
  3. spring容器创建数据源,设置属性时发现sealed = true,抛出异常
  4. 服务启动失败

回到最开始的现象,特别是在没有进行灰度发布的场景下,基本上很难正常的启动成功 。在灰度发布时,是服务启动后,才会转发部分流量到灰度容器,所以dubbo是否提前暴露并没有影响,因为在启动过程中并没有请求进入。但是在非灰度发布场景下,由于dubbo服务一暴露,马上就有请求进入,所以导致启动异常。

解决

知道问题产生的原因,就很容易解决问题了,解决方式很简单,让dubbo的服务在spring容器启动后暴露即可,从dubbo的文档可以看到,2.6.5之后不会再出现这种弱智问题了,但是我们的服务比较旧,用的2.6.3的版本,所以需要配置delay=-1,或者设置一个较长的delay时间。

相关推荐
软件技术NINI9 分钟前
基于SpringBoot和Vue的摇滚乐鉴赏网站
vue.js·spring boot·后端·spring·html·毕业设计·毕设
Tsuki_tl26 分钟前
【总结】Redis五大数据类型应用场景及基本命令
数据库·redis·后端·缓存·缓存中间件·redis命令·redis 五大数据类型
后台模板学习29 分钟前
Go Web开发中任务调度框架的源码解析与高可用实现方案
开发语言·后端·golang
系统集成-鹰翔32 分钟前
ERP对接升级提示数据库迁移失败:SQLite逐版本事务到底回滚到哪一步
javascript·数据库·经验分享·后端
西瓜拿铁好喝32 分钟前
开源和闭源 AI 办公工具,分别适合谁
java·前端·数据库
IT_陈寒32 分钟前
深入理解SpringBoot:核心原理与最佳实践
前端·人工智能·后端
学长毕设34 分钟前
基于SpringBoot的校园礼服租赁系统的设计与实现
java·spring boot·后端
明天也要努力学习40 分钟前
深入理解 JVM 垃圾回收(GC)
java·jvm
golang学习记1 小时前
IDEA 断点调试技巧:拖拽
java·ide·intellij-idea
Sayai1 小时前
Flink 日志解析任务性能优化实战:正则/Grok 预编译、Guava 缓存权重陷阱与 ES 写入隔离
java·大数据·性能优化·flink·实时计算