SpringBoot中定时任务踩坑,@Scheduled重复执行问题排查(看完直接破防)

前言

今天再开发业务需求的过程中,需要用到定时任务,原本定的是每10分钟推送一次,可是当每次十分钟到的时候,定时任务就会推送多条!但是非常奇怪的是,本地调试的时候不会有问题,只有当你部署到到服务器上的时候才会暴露这个问题!!!!

如图: 这些消息都是一次性推送出来的,本来他们应该只有一条被推送出来的,可是现在他们却全都出来了,难道真是"一家人就要整整齐齐"吗?

解决思路

这种本地无法重现的问题解决起来最恼火了,于是乎我去网上寻找答案,看看有没有人遇到类似的情况! 其中两篇是这么说的:

文章连接:生产问题:@Scheduled Spring定时任务每次执行两次原因分析以及解决方案

文章连接:spring定时任务执行两次的原因与解决方法

他们的大概的意思就是因为他的配置到导致他的配置类被重复加载了两次,进而导致定时任务重复执行多次!

于是我就类比了一下,看到在我的项目中,确实使用了@EnableScheduling两次

第一次:

第二次:

于是我赶紧把启动类上的@EnableScheduling注解去掉,并祈祷能有用!

结果如我所料,果然没什么用!

于是我继续搜索,看到这样一篇文章:

当我看到这句话的时候,我甚至以为我已经接近真理了! springboot关于定时任务执行多次的问题 但是当我看到他写的这些太乱了,而且一个简答的定时任务,被他搞得这么复杂,我果断放弃了。

头痛砍头????

我于是乎又接着找,我发现了这样一篇文章,特别逆天!!!

Springboot 使用 @Scheduled 定时任务生产环境执行两次 好家伙,你这好比你去医院看病,你和医生说:"医生我头痛,我该怎么办?",医生说:"没事的哈,一会去把把头砍了,砍了就不痛了哈!"

接近真理

时间在一分一秒的过去,我却毫无紧张,甚至已经开始汗流浃背了,从未感到如此巨大之强度,于是乎我便去到了stackoverflow上搜索一下看看!

哎!你说好巧不巧,还真就让我找到问题了! 看到这样一条问题:

这不就是我遇到的问题吗?

他是这么说的:

I have a service which has to run a job to get and refresh it's data from another service. The job has to be run on startup and every couple of hours/days. I was looking into the behavior of the scheduled job and it seems to be called two times consecutively according to the logs (see below).

我们来看他给的代码:

java 复制代码
@Service
public class ServiceImpl implements ServiceInterface {

@Autowired
private FetchService fetchService;

private int timesCalled = 0;
private Data data;

@PostConstruct
private void initialize() {
    data = fetchService.getAndUpdate();
}

@Scheduled(cron = "* */5 * * * *")
private void refresh() {
    LOG.info(appContext.getId());
    LOG.info("This object: " + System.identityHashCode(this));
    LOG.info("Times called: " +  timesCalled);
    timesCalled++;
    data = fetchService.getAndUpdate();
}

日志信息:

yaml 复制代码
2020-07-02 17:30:00.006  INFO 30416 --- [   scheduling-1] c.d.p.d.service.ServiceImpl  : org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6db9cae5
2020-07-02 17:30:00.006  INFO 30416 --- [   scheduling-1] c.d.p.d.serice.ServiceImpl  : This object: 357813323
2020-07-02 17:30:00.006  INFO 30416 --- [   scheduling-1] c.d.p.d.service.ServiceImpl  : Times called: 1
....
2020-07-02 17:30:32.001  INFO 30416 --- [   scheduling-1] c.d.p.d.service.ServiceImpl  : org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6db9cae5
2020-07-02 17:30:32.001  INFO 30416 --- [   scheduling-1] c.d.p.d.service.ServiceImpl  : This object: 357813323
2020-07-02 17:30:32.001  INFO 30416 --- [   scheduling-1] c.d.p.d.service.ServiceImpl  : Times called: 2

然后就有网友给他解释道:正如你所写的,你的cron表达式是 * */5 * * * * ,这意味着,根据Spring指南,它将每隔5分钟运行一次:

你写的代码不就是下面这样吗?

java 复制代码
@Scheduled(cron = "* */5 * * * *")
private void refresh() {
    log.info("Times called: " +  timesCalled);
    timesCalled++;
}

我们来测试一下吧:

yaml 复制代码
14:20:13.001  INFO 8980 --- [   sch-1] com.example.demo.Sched   : Times called: 13
14:20:14.001  INFO 8980 --- [   sch-1] com.example.demo.Sched   : Times called: 14
14:20:15.003  INFO 8980 --- [   sch-1] com.example.demo.Sched   : Times called: 15
14:20:59.002  INFO 8980 --- [   sch-1] com.example.demo.Sched   : Times called: 59
14:25:00.000  INFO 8980 --- [   sch-1] com.example.demo.Sched   : Times called: 60
14:25:01.000  INFO 8980 --- [   sch-1] com.example.demo.Sched   : Times called: 61
14:25:02.001  INFO 8980 --- [   sch-1] com.example.demo.Sched   : Times called: 62
14:25:03.002  INFO 8980 --- [   sch-1] com.example.demo.Sched   : Times called: 63

从日志我们可以看出,他确实是在每五分钟执行一次,可是执行完了,他在第五分钟内还将重复执行!所以我们会看到业务代码被执行了很多次!如果你想每五分钟只执行一次的话,你应该这样写:@Scheduled(cron = "0 */5 * * * *")

成功解救

我一看我的代码也是这样写的:

赶紧改过来,问题就解决了!这下对spring的corn表达式理解又加深了!同时有问题可去stackoverflow上搜一下,很有用!!

最后的疑问

为什么本地调试我0 */5 * * * *这样写和* */5 * * * *都只执行一次,而到线上的时候就会执行多次,有谁能知道这是为什么?

本文由mdnice多平台发布

相关推荐
谁在黄金彼岸2 小时前
nginx服务化五套方案原理解剖与选型
后端
谁在黄金彼岸2 小时前
WinSW在Win7上失败真相-实测与修复
后端
花间相见2 小时前
【后端开发|Redis进阶01】—— Redis分布式锁全解:从SETNX到Redisson看门狗,再到RedLock
后端·面试
llqbzllll3 小时前
A2A 1.0 到底和 MCP 差在哪:从 Agent Card、Task 到 Java 最小互操作
后端
花间相见3 小时前
【后端开发|JVM基础01】—— Java垃圾回收全解:从分代内存模型到各收集器的分代职责
后端
geovindu3 小时前
rust: Abstract Factory pattern
开发语言·后端·设计模式·rust·抽象工厂模式
pe7er3 小时前
Spring Boot 日志最佳实践:接入、分级、异常记录与滚动拆分
后端
程序员老赵4 小时前
Docker 部署 Dolibarr:轻松搭建开源 ERP/CRM 平台
运维·前端·后端
量化分析码农5 小时前
【Python量化系统工程实战 #06】从本地脚本到云端部署:量化系统的最小可运行架构
后端