掘金 后端 ( ) • 2024-04-15 18:04

前言

本片文章主要介绍了,实际开发中因为项目架构的问题,引发的一系列让人摸不做头脑的问题。实际上不是技术框架bug,和开发同事的写法问题。

问题的前因后果:

今年的项目的定时任务都是用的quartz框架。在开发过程中,没遇到啥问题。但是测试在测试的时候,出现定时任务偶尔不执行,定时任务执行报错。 最开始在测试环境有这个报错的问题,估计定时任务是其他人的电脑在执行(当时问了开发他们说他们没启动)。

这样过了大半个月,测试发现在生产上也出现定时任务不执行,执行报错的问题,而且我们同事还在说,我明明改了代码,本地也没问题,但是生产还是执行的以前的逻辑呢,给技术领导反馈了,也不知道咋回事。因为这半个月以来我一直在看quartz框架的原理,还写过相关文章【quartz在springboot中启动流程】。大家都发现了定时任务有问题,这也激发了我的兴趣。因为有了知识储备,所以我也是解决的这两个问题。


原因分析:

  1. 定时任务偶尔不执行 首先这个并不是任务没有执行,而是没有捕捉异常,没有去更新数据状态导致的。下面看看代码吧

      @Override
      public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
      	//查询需要到执行导出的任务
        List<ExportRecord> unionImportExportVos = service.getExportingRecord();
        ExportService exportService = null;
        for (ExportRecord record : unionImportExportVos) {
        //获取实现类
          exportService = (UnionExportService) applicationContext.getBean(record.getImplementBeanName());
          //实现类执行导出任务,导出成功更新 record 导出状态
          exportService.exportData(unionImporExportVo.getId());
        }
      }
    

    由于实现类是由多个人写的,有些没有捕捉异常,并且excute接口也没有去捕捉异常,导致ExportRecord的状态一直是导出中,让测试觉得定时任务没有执行。

    所以第一个问题是在各自的实现类、以及execute方法里面 增加捕捉异常,并且更新record为失败的状态。

    改了之后,偶尔还是会出现问题,并且从来没有找到过日志。分析下面几个问题时会找到没有没有报错日志的原因。

  2. 改了代码,生产环境还是执行以前的逻辑 听见同事在反馈,这时我已经对quartz有了一定的了解。回头再去排查,就找到了根本原因。不是代码的问题,是系统架构问题,还有另一个系统也在跑我们的的定时任务

    说一下为什么另一个项目为什么会跑我们的项目吧,因为这两个项目用的是统一套代码,同一个数据库。只是在表上面加了前缀做了隔离,但是定时任务内部实际记录任务状态的表又没有加前缀。所以两个任务串门了。

    当时同事和技术leader说明的时候,他们说加了前缀的,实际上加了前缀的这个表不是quartz框架内部的,只是第三方框架对外暴露对定时任务进行增删改查的表。

  3. 定时任务偶尔报错,找不到用户信息。 解决了同事代码没生效问题(改了定时任务两个项目都打包就没问题了),发现我的定时还是出现找不到用户信息的错误。最开始我以为我的写法有问题,userId 存到TheadLocalMap中,后面加了日志发现实在查询数据库的时候mapper.selectById(userId) 没有查询到。去数据库查却有,没错出现这个问题还是因为在两个项目用的同一套代码的问题。导致这个mapper也给老子用串了,查到另一个项目的user表了。

    最后还是把定时任务这块完全独立开了,但是代码用的是一个git,两个项目模块存在互相调用的情况,因此估计后续还可能出现其他问题。(比如出现要使用到两个系统同名的表,并且mapper名称一样,这时候系统就查自己的表去了)


解决方案:

将Quartz相关所有的表都彼此独立开,就解决了quartz任务执行串门的问题。 主要还是项目架构出了大问题(两个项目用一套代码,只是分布在不同的module,连数据库都用的一样,只是加了前缀做区分,打包的时候两个模块的代码都打进去了),估计后面还会踩很多坑。

根本原因

项目架构设计出了问题。

为什么要这样做呢? 当时本来是两个项目,在开发第二项目的时候发现要频繁调用第一个项目的接口,有没有用微服务,用http太麻烦了。没办法就把两个项目的代码合到一起了,但是部署、表还是分开的。 有人说为啥当时不做到一个项目呢? 哈哈。因为当时业务领导的指导思想是,我每个子系统都要能独立运行,都要可以直接售卖。而且开发的时候,我们也没有项目设计时间,拿到就开整