工作思考|研发环境好好的,怎么上线就出问题了?
场景再现
那是一个夜黑风高的晚上,某个版本迭代经过了完备的测试,正准备上线。研发同事A开完了上线评审后,信心满满地对运维同事B说:“开冲!”
几分钟后,同事B发了条消息过来,看着抖动的头像,同事A心想:小B效率真高啊,这么快!点开消息一看【启动报错了,你看一下】。
什么?启动还能报错,不可能啊,我研测环境都好好的。
小A火急火忙地连上堡垒机,看了下日志,报错信息大致为 【表tb_xxx没有找到】。
“怎么可能,我用了伟大的flyway,怎么可能会没有表呢?”小A如是说道。
提到flyway,这里简单介绍一下。Flyway是一款开源的数据库版本管理工具,可以实现管理并跟踪数据库变更,支持数据库版本自动升级,而且不需要复杂的配置,能够帮助团队更加方便、合理的管理数据库变更。只需要引入相应依赖,添加配置,再在resource目录下创建db/migration/xxxx.sql文件,在sql文件中写入用到的建表语句,插入语句即可。
不管怎么说,代码是不会骗人的。先找下是哪里出了问题!
小A很快就定位到了代码位置,是一个用于缓存的HashMap,这操作也没什么问题,相信大家都这么用过,对于一些一次查找,到处使用,还亘古不变的表信息,可以先查一次,把它用Map缓存起来,以便后续使用。
但是研发同事C把这段代码放在了afterPropertiesSet()
方法内部,没错,就是那个InitializingBean
接口的方法。看到这里,相信各位熟练背诵Bean生命周期的Java Boy已经明白了!查询数据库的操作在Bean还没有创建完成的时候就进行了!而此时,flyway脚本还没有执行,自然就找不到对应的表信息了。
那怎么办呢?
解决方法
解决方法很简单,sql执行的时候找不到表,那就让它在表创建完之后再执行!
1.CommandLineRunner接口
一个方法就是我们常用的CommandLineRunner接口,重写run()方法,把缓存逻辑移到run()方法中。原因是run()方法的执行时机是在SpringBoot应用程序启动之后,此时flyway已经执行完毕,表结构已经存在,就没问题了!
2.@DependsOn注解
通过代码分析,flyway的加载是由flywayInitializer这个Bean负责的。所以只需要我们的Bean在它之后加载就行了,这就用上了@DependsOn注解。
@DependsOn注解可以定义在类和方法上,意思是我这个Bean要依赖于另一个Bean,也就是说被依赖的组件会比该组件先加载注册到IOC容器中。
也就是在我们的Bean上加上这么个注解@DependsOn("flywayInitializer")
总结
此次线上问题复习了Bean的生命周期,复习了InitializingBean
,CommandLineRunner
两个接口,复习了@DependsOn
注解。
来源:juejin.cn/post/7349750846898913332