前言
今天再开发业务需求的过程中,需要用到定时任务,原本定的是每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).
我们来看他给的代码:
@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();
}
日志信息:
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分钟运行一次:
你写的代码不就是下面这样吗?
@Scheduled(cron = "* */5 * * * *")
private void refresh() {
log.info("Times called: " + timesCalled);
timesCalled++;
}
我们来测试一下吧:
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 * * * *
都只执行一次,而到线上的时候就会执行多次,有谁能知道这是为什么?