前言
本篇文章主要介绍了,实际开发中因为项目架构的问题,引发的一系列让人摸不着头脑的问题。实际上不是技术框架bug,和开发同事的写法问题。(ps:本篇文章主要记录了在Quartz使用过程,因为用法问题引起小问题)
问题的前因后果:
今年的项目的定时任务都是用的quartz框架。在开发过程中,没遇到啥问题。但是测试在测试的时候,出现定时任务偶尔不执行,定时任务执行报错。 最开始在测试环境有这个报错的问题,估计定时任务是其他人的电脑在执行(当时问了开发他们说他们没启动)。
这样过了大半个月,测试发现在生产上也出现定时任务不执行,执行报错的问题,而且我们同事还在说,我明明改了代码,本地也没问题,但是生产还是执行的以前的逻辑呢,给技术领导反馈了,也不知道咋回事。因为这半个月以来我一直在看quartz框架的原理,还写过相关文章【quartz在springboot中启动流程】。大家都发现了定时任务有问题,这也激发了我的兴趣。因为有了知识储备,所以我也是解决的这两个问题。
原因分析:
-
定时任务偶尔不执行 首先这个并不是任务没有执行,而是没有捕捉异常,没有去更新数据状态导致的。下面看看代码吧
@Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { //查询需要到执行导出的任务 List<EptRecord> records = service.getRecords(); eptService = null; for (EptRecord record : records) { //获取实现类 eptService = (EptService) applicationContext.getBean(record.getImplementBeanName()); //实现类执行导出任务 eptService.export(record.getId()); // 导出成功更新 record 导出状态 eptService·updateEptSuc(record.getId); } }
没有捕捉异常,所以在执行export的实现的时候可能就发生了问题,后面导出记录里面就像没有记录任何状态状态,就像任务没有执行的一样。
正确的做法是,捕捉异常,记录导出记录的异常状态。其实对于这种,感觉任务没有执行的情况,我们其实可以直接看
qrtz_triggers
表任务执行的时间,里面有量子字段分别记录了任务上次的执行时间,还有任务下次执行的时间PREV_FIRE_TIME
,NEXT_FIRE_TIME
,还有个状态也很重要TRIGGER_STATE
,这个状态记录任务的一个状态 等待执行的时候是WAITING
,如果任务发生了异常停止执行会是ERROR
状态; -
过了一段时间,听见同事在反馈,生产环境还是执行以前的逻辑,让同事也手足无措。 这时我已经对quartz有了一定的了解。回头再去排查,就找到了
根本原因
。不是代码的问题,是系统架构
问题,还有另一个系统也在跑我们的的定时任务说一下为什么另一个项目为什么会跑我们的项目吧,因为这两个项目用的是统一套代码,同一个数据库。只是在表上面加了前缀做了隔离,但是定时任务内部实际记录任务状态的表又没有加前缀。所以两个任务串门了。
当时同事和技术leader说明的时候,他们说加了前缀的,实际上加了前缀的这个表不是quartz框架内部的,只是第三方框架对外暴露对定时任务进行增删改查的表。
-
定时任务偶尔报错,找不到用户信息。 解决了同事代码没生效问题(改了定时任务两个项目都打包就没问题了),发现我的定时还是出现找不到用户信息的错误。最开始我以为我的写法有问题,userId 存到TheadLocalMap中,后面加了日志发现实在查询数据库的时候
mapper.selectById(userId)
没有查询到。去数据库查却有,没错出现这个问题还是因为在两个项目用的同一套代码的问题。导致这个mapper也给老子用串了,查到另一个项目的user
表了。最后还是把定时任务这块完全独立开了,但是代码用的是一个git,两个项目模块存在互相调用的情况,因此估计后续还可能出现其他问题。(比如出现要使用到两个系统同名的表,并且mapper名称一样,这时候系统就查自己的表去了)
解决方案:
将Quartz相关所有的表都彼此独立开(quartz配置支持修改quartz相关表的前缀),就解决了quartz任务执行串门的问题。 主要还是项目架构出了大问题(两个项目用一套代码,只是分布在不同的module,连数据库都用的一样,只是加了前缀做区分,打包的时候两个模块的代码都打进去了),估计后面还会踩很多坑。
根本原因
项目架构设计出了问题。
为什么要这样做呢? 当时本来是两个项目,在开发第二项目的时候发现要频繁调用第一个项目的接口,有没有用微服务,用http太麻烦了。没办法就把两个项目的代码合到一起了,但是部署、表还是分开的。 有人说为啥当时不做到一个项目呢? 哈哈。因为当时业务领导的指导思想是,我每个子系统都要能独立运行,都要可以直接售卖。而且开发的时候,我们也没有项目设计时间,拿到就开整