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

相关推荐
一 乐41 分钟前
婚纱摄影网站|基于ssm + vue婚纱摄影网站系统(源码+数据库+文档)
前端·javascript·数据库·vue.js·spring boot·后端
码事漫谈2 小时前
Protocol Buffers 编码原理深度解析
后端
码事漫谈2 小时前
gRPC源码剖析:高性能RPC的实现原理与工程实践
后端
踏浪无痕3 小时前
AI 时代架构师如何有效成长?
人工智能·后端·架构
程序员小假4 小时前
我们来说一下无锁队列 Disruptor 的原理
java·后端
武子康5 小时前
大数据-209 深度理解逻辑回归(Logistic Regression)与梯度下降优化算法
大数据·后端·机器学习
maozexijr5 小时前
Rabbit MQ中@Exchange(durable = “true“) 和 @Queue(durable = “true“) 有什么区别
开发语言·后端·ruby
源码获取_wx:Fegn08955 小时前
基于 vue智慧养老院系统
开发语言·前端·javascript·vue.js·spring boot·后端·课程设计
独断万古他化5 小时前
【Spring 核心: IoC&DI】从原理到注解使用、注入方式全攻略
java·后端·spring·java-ee
毕设源码_郑学姐5 小时前
计算机毕业设计springboot基于HTML5的酒店预订管理系统 基于Spring Boot框架的HTML5酒店预订管理平台设计与实现 HTML5与Spring Boot技术驱动的酒店预订管理系统开
spring boot·后端·课程设计