情境(Situation)
项目所有的定时任务和业务都在同一个应用中,现在甲方提出需求:为避免在升级发布时程序异常导致batch无法正常执行。所以要把batch从原程序中分离出来。但是需要一个一个的分离。由于项目中的quartz框架用的是数据库模式,并且新程序和原程序使用的是同一个数据库,这导致了程序之间的source不一致。而quartz随机分配任务,没有分离的batch可能会被分配到新程序中导致batch执行失败。
任务(Task)
与项目组成员讨论解决方案,分析每个方案的利弊,选择合适的方案并且实施。
行动(Action)
方案提出
我们讨论出的解决方案主要是三种:
1. 分离出来的batch使用新的框架管理(Spring Task、Spring Batch)。
2. 取消quartz框架,使用shell定时调用。
3. 加一个数据库给新程序的quartz框架使用。
方案选择
第一个方案需要花费的调查时间较长,而且能否顺利实施,实施完成后还需要测试框架的正确性。所以被pass掉了。
第二个方案取消掉quartz看起来是很好的方案,但是有两个不好的地方。其一,不能使用集群模式;其二,shell调用启动应用会导致cpu占用高。
第三个方案就比较合理一些,因为在把所有的batch分离完成过后,还可以把新程序的quartz的数据库移回至原数据库。而且添加一个数据源的时间不会很长。
方案实施
方案三的流程图:
参照Mybatis plus 配置多数据源把项目的多数据源配置好了,给quartz指定了新配置的数据源。
开跑,batch业务执行时报错了。后来发现是因为项目中使用了Mybatis-plus框架,而在之前写多数据源时提供给Spring的SqlSessionFactory是原生的,而不是MybatisSqlSessionFactory,导致了Mybatis-plus不兼容。要问我怎么发现的,我只能说有人踩过坑了✈️。mybatis plus报Invalid bound statement (not found):解决
结果(Result)
最后项目运行正常,没有和原程序的batch相互干扰。这也是我第一次用多数据源,感觉还是很简单的。
总结(Summarize)
使用@Primary注解让Mybatis默认使用我们自定义的DynamicDataSource,在生成DynamicDataSource对象时,我们把多个的数据源都以map的形式保存到了DynamicDataSource对象中。后面切换数据源主要是通过改变DataSourceContextHolder类中的contextHolder成员保存的key,而在需要进行数据库连接时,Mybatis在DynamicDataSource中索取数据源,DynamicDataSource通过determineTargetDataSource()方法从map中获取具体的数据源。而key是通过重写的determineCurrentLookupKey()方法在DataSourceContextHolder类中的contextHolder成员那里拿到的。这样就达到了数据源切换的效果了。总之就是有些东西用了就知道很简单。
更优方案
在看了quartz配置参数后发现还有一个更好的方案:修改数据库表的前缀,通过表名前缀也可以达到batch隔离的效果。