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多平台发布

相关推荐
我学上瘾了5 小时前
Spring Cloud的前世今生
后端·spring·spring cloud
波波0076 小时前
ASP.NET Core 健康检查实战:不只是一个 /health 接口
后端·asp.net
小码哥_常6 小时前
Spring Boot 搭建邮件发送系统:开启你的邮件自动化之旅
后端
石榴树下的七彩鱼7 小时前
图片修复 API 接入实战:网站如何自动去除图片水印(Python / PHP / C# 示例)
图像处理·后端·python·c#·php·api·图片去水印
我叫黑大帅7 小时前
为什么TCP是三次握手?
后端·网络协议·面试
我叫黑大帅8 小时前
如何排查 MySQL 慢查询
后端·sql·面试
techdashen8 小时前
Rust项目公开征测:Cargo 构建目录新布局方案
开发语言·后端·rust
消失的旧时光-19438 小时前
Spring Boot 实战(五):接口工程化升级(统一返回 + 异常处理 + 错误码体系 + 异常流转机制)
java·spring boot·后端·解耦
Rust研习社8 小时前
Rust 智能指针 Cell 与 RefCell 的内部可变性
开发语言·后端·rust
夕颜1119 小时前
Skill 机器人 vs Hermes Agent:两种「AI 越用越聪明」的路径
后端