这篇文章主要讲解了“如何搭建基于Spring Task实现动态管理任务”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何搭建基于Spring Task实现动态管理任务”吧!
定时任务是指调度程序在指定的时间或周期触发执行的任务,常用场景如下:
定时发短信
定时变更数据
定时统计数据
定时修改状态
定时开始活动等
Timer
简介:Timer 是 Jdk自带的定时任务执行类,无论任何项目都可以直接使用 Timer 来实现定时任务,所以 Timer 的优点就是使用方便。
原理:
调度器:单线程。
任务存储:最小堆实现任务存储。
优点:Jdk自带类,无需引入其他Jar,简单易用。
缺点:Timer中的多个任务只能使用一个线程去执行,因此任务之间的执行情况会相互影响。
当一个任务的执行时间过长时,会影响其他任务的调度任务异常影响其他任务。
当一个任务抛出异常,其他任务也会终止运行.
结论:基本无人使用。
简介:ScheduledExecutorService 是JDK里面自定义的几种线程池中的一种,支持多线程并发的去执行多个调度任务,弥补了Timer的缺陷。
原理:
调度器:多线程。
任务存储:最小堆实现任务存储。
优点:Timer能做到的事情ScheduledExecutorService都能做到,且完美的解决上面所说的Timer存在的两个问题。
缺点:只支持固定速率(fixed-rate)或固定延迟(fixed-delay)的调度任务,不灵活。
结论:常用于框架内部定时任务。
描述:Spring Framework 自带的定时任务。
优点:同ScheduledExecutorService,同时增加了支持cron表达式,可以配置任意基于时钟的调度任务。
缺点:
不支持动态修改任务状态、暂停/恢复任务,以及终止运行中任务。
不支持在线监控执行的任务。
原理:ScheduledExecutorService的扩展。
结论:常用于中小型企业,作为单机定时任务使用。
以上都是单机版本。
其他分布式定时任务诸如:quartz、xxl-job、elastic-job等等,功能、性能都很强劲,这里不作为研究对象,详情参考:
Java定时任务框架对比
定时任务实现原理 最小堆 时间轮
上面的这些框架都不是我想选择的,要想自由的掌控雷电,那就自己造个简易轮子,满足90%需求即可。
期望实现如下特性:
轻量、轻量、轻量。
支持在线监控执行的任务。
支持动态修改任务状态、暂停/恢复任务,以及终止运行中任务。
支持在线配置调度任务入参和。
支持集群环境扩展(可选)。
收集了半天信息,直接使用Spring Task就可以实现,仅依赖Spring Boot。
代码示例:
@Component @EnableScheduling // 开启定时任务 public class DemoApplication { // 添加定时任务 @Scheduled(cron = "0/5 * * * * *") // cron 表达式,每5秒执行 public void doTask(){ System.out.println("我是定时任务~"); } }
无法动态修改任务状态、暂停/恢复任务,以及终止运行中任务。
关键技术点和坑
Spring Task的调度器默认是线程数为1的ThreadPoolTaskScheduler,自动装配类为TaskSchedulingAutoConfiguration,多任务之间的执行会相互影响,一定要修改默认值。
通过TaskScheduler接口,可以扩展实现动态修改任务状态、暂停/恢复任务,以及终止运行中任务。
TaskScheuler是在Spring 3.0中引入的,有多种方法可以在将来的某个时刻运行,它还返回ScheduledFuture接口的对象,可用于取消计划的任务或检查任务是否完成。
cron-utils一个Java库,用于解析,验证Cron表达式,可以去GitHub查看详细说明。
实现设计
定义IJob接口,用于客户端描述任务
public interface IJob { void execute(JobContext map) throws JobException; }
定义注解,用于配合IJob接口定义任务
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Job { @AliasFor(annotation = Component.class) String value() default ""; /** * cron 表达式默认不"-"代表不执行 */ String cron() default "-"; /** * 任务编码 必须唯一 */ String taskCode(); /** * 任务名称 */ String taskName(); }
定义运行任务状态
public class Task{ /** * 任务的编码 必须全局唯一 */ private String taskCode; /** * 任务的名称 */ private String taskName; /** * 任务的类名称 */ private String taskClassName; /** * 任务的cron表达式 */ private String taskCron; @JsonIgnore private ScheduledFuture scheduledFuture; @JsonIgnore private IJob job; private TaskStateEnum taskState;
定义任务存储接口,用于存储在缓存或者DB中
public interface ITaskStore { void saveTask(Task task); List<Task> list(); Task updateTaskByTaskCode(String taskCron, String taskName, String taskCode); Task updateTaskStateByTaskCode(TaskStateEnum taskState, String taskCode); void deleteTaskByTaskCode(String taskCode); Task findByTaskCode(String taskCode); }
定义任务锁接口,解决并发问题,以及扩展支持集群环境
public interface ILockService { void lock(String taskCode); void unlock(String taskCode); }
定义事件监听器,用于监听任务的状态事件,可扩展状态监控,各种回调等
public interface IEventListener { void listener(Event event); }
核心处理器,处理核心流程
初始化加载所有IJob的实现 从Spring容器获取IJob实现类并解析Job注解
添加任务threadPoolTaskScheduler.schedule(task,cron)
更新任务详情
scheduledFuture.cancel(true)
threadPoolTaskScheduler.schedule(task,cron)
启动任务 threadPoolTaskScheduler.schedule(task,cron)
暂停任务 scheduledFuture.cancel(true)
任务监控 TaskList
待实现功能
重试补偿:失败重试。
failstore : 存储失败任务,供人肉补偿。
misfire:存储错过的任务,供人肉补偿。
自己在核心处理器中加下相应的增强功能逻辑即可。
直接实现IJob接口并加上Job注解即可
@Job(taskCode = "job1", taskName = "laker测试任务",cron = "0/5 * * * * *") @Slf4j public class TestJob implements IJob { @Override public void execute(Map map) throws Exception { log.info("laker job run"); TimeUnit.SECONDS.sleep(10); } }
感谢各位的阅读,以上就是“如何搭建基于Spring Task实现动态管理任务”的内容了,经过本文的学习后,相信大家对如何搭建基于Spring Task实现动态管理任务这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。