@Scheduled任务失效问题

599 阅读2分钟

背景

前段时间重构一个陈年老系统,顺便将jdk由7升级到了8,将spring从3.x升级到4.x,其他组件如MyBatis、swagger等也做了些升级。系统上线后,运行正常,皆大欢喜,这破系统总算也可以开心的写lambda了。

然而,好景不长,某日意外发现有个定时任务没有如期执行;再看下监控,定时任务失效的日期正是上线那天😳。慌乱之际,不由得庆幸并未造成资损,不然后果不堪设想啊!

问题分析

应用中定时任务是用的spring的@Scheduled注解实现的,在此次系统重构之前一直是正常执行的,本次重构也未改动。想必是spring升级导致的新旧版本兼容问题了。

对比了另外两个正常运行的task,找到了一些蛛丝马迹。

且看下这个task的代码:

@Controller
public class RefreshDictionaryJob {

    private static final Logger logger = LoggerFactory.getLogger(RefreshDictionaryJob.class);

    @RequestMapping(value = "/demo/dictionary/edit", method = RequestMethod.POST)
    @ResponseBody
    public String edit(XxxVO params) {
        try {    
            // 略去若干行……
        
            return "更新成功";
        } catch (Exception e) {
            logger.error("更新失败", e);
            return "更新失败,请稍后再试";
        }
    }

    @Scheduled(cron = "0 0/5 *  * * ? ")
    public void refreshDictionary() {
        try {
           // do something
        } catch(Exception e){
            logger.error("refreshDictionary执行失败" + e);
        }
    }
}

是不是很诡异?类命名为XxxJob,却加了个@Controller注解;同一个类中混合了@RequestMapping@Scheduled方法,整体上比较混乱;而其他正常的task都是在@Component标注的类下。

测试一下试试吧,把@Controller替换为@Component,本地试一把————等待一会儿后,任务并未如期执行。 把@RequestMapping@ResponseBody都注释掉,再试试看————果不其然,任务执行了! 翻了下spring关于@Scheduled的官方文档,并未提到这些限制,怀疑是spring4在生成代理类时产生了冲突。趁没有造成损失,赶紧先修复上线吧,否则领导那没法交代了! ̄□ ̄||

问题修复

尘归尘,土归土,Controller的归Controller,代码拆解调整如下:

@Component
public class RefreshDictionaryJob {

    private static final Logger logger = LoggerFactory.getLogger(RefreshDictionaryJob.class);

    @Scheduled(cron = "0 0/5 *  * * ? ")
    public void refreshDictionary() {
        try {
           // do something
        } catch(Exception e){
            logger.error("refreshDictionary执行失败" + e);
        }
    }
}

@Controller
@RequestMapping("/demo/dictionary")
public class DictionaryController {
    private static final Logger logger = LoggerFactory.getLogger(DictionaryController.class);
    
    @RequestMapping(value = "/edit", method = RequestMethod.POST)
    @ResponseBody
    public String edit(XxxVO params) {
        try {    
            // 略去若干行……
        
            return "更新成功";
        } catch (Exception e) {
            logger.error("更新失败", e);
            return "更新失败,请稍后再试";
        }
    }

}

总结

编码不规范真是害死人啊,处处坑!

关于问题的根本原因,希望了解的朋友给个指引,感谢!