「这是我参与11月更文挑战的第9天,活动详情查看:2021最后一次更文挑战」
引言
最近小编一直在加班写需求,没有时间更文。项目中有一个需求,觉得挺有意思的,把做的思路分享给大家,需求是每个月的8号和20号我们系统会处理一些单子,这里将这两天称为审单日,如果超过这个时间的单子,需要系统在9号和20号的12:30,自动将单子撤销掉。如果8号或20号遇到周末,则需要提前或者延后到工作日作为审单,第二天的中午12:30做撤销单据的任务。
设计思路
针对这个问题我想了两个实现思路。
动态定时任务(放弃)
创建一个动态定时任务,每次执行任务成功后,修改定时任务的cron表达式,更新其下次执行的时间。处理撤销单据和获取撤销单据日的cron表达式同上面设计思路一致。
优点:
- 只在需要的时间去执行代码
缺点: - 代码实现逻辑相对复杂
- 担心Elastic-Job出现问题后的处理成本更大
定时任务-12:30分执行(使用)
设置一个定时任务,每天12:30分执行,创建一张表预置撤销单据日期,每次任务执行时,判断当前日期是否是撤销单据日,如果不是直接跳过,如果日期一致的话,则执行撤销单据的操作,并且将这一条撤销单据时间状态修改为已执行。
优点:
- 实现代码逻辑特别容易
缺点: - 不够灵活,每日都需要执行一次定时任务,不能按照需要去执行
撤销时间表
1 | sql复制代码-- auto-generated definition |
初始化sql
1 | sql复制代码INSERT INTO saos_csp_fop.fop_cancel_schedule (id, title, execute_time,cron, status, remark, created_at, modified_at) VALUES (1, '2021年11月的对公付款日后的取消', '2021-11-22','0 30 12 9 * ? *', 1, null, '2021-11-22 15:14:22', '2021-11-22 15:14:22'); |
动态定时任务
初始化配置(DynamicElasticJobConfig)
1 | java复制代码import com.hanhang.service.listener.ElasticJobListener; |
动态定时任务相关操作工具(ElasticJobHandler)
1 | java复制代码package com.hanhang.service.handler; |
overwrite(true)此处是开启job可被重写,方便修改任务的cron表达式
配置ElasticJobListener监听器(ElasticJobListener)
1 | java复制代码package com.hanhang.service.listener; |
动态任务执行类(PaymentCancelDynamicJob)
1 | java复制代码package com.hanhang.service.job; |
@Transactional(rollbackFor = Exception.class)在execute方法加事务,会导致启动项目失败
扫描本地持久化的任务、添加任务(ScanDynamicJobHandler)
1 | java复制代码package com.hanhang.service.handler; |
项目启动程序中新增加载本地持久化任务
1 | swift复制代码package com.hanhang; |
至此动态修改定时任务可以完成,通过配置界面也可以看到任务。
问题
1、通过代码测试,修改任务的cron会成功,定时任务也会按时去执行,但是在管理页面中看不到cron表达式修改。
2、如果Elastic-Job不通,或在定时的时间内重启服务,由于任务有【错过重执行】,那么有可能在撤销了不该撤销的单据。
3、Elastic-Job出现异常,无法做幂等。
综合以上的问题,放弃了这种方式。另一种实现方式就不在文章中体现了,处理逻辑比较简单。
总结
使用Elastic-Job动态修改定时任务,可能踩到的坑有:
1、在execute方法上使用@Transactional注解导致服务启动失败,报nullException。
需要用其他方式进行事务处理,请参照这篇文章@Transactional注解失效
2、在scanJob的时候,需要通过注入的方式,将Job添加到SpringJobScheduler中,否则在Job执行中注入的属性,将不会被Spring代理,出现空指针异常。
3、修改cron之后,在管理页面看不到cron表达式变更,但是通过Elastic-Job通过时间片的轮转,也可以正常执行。
本文转载自: 掘金