【Java之轨迹】SpringBoot 实现动态往数据库中添加时间点,使程序在规定的时间点执行任务(定时任务变形应用,附实战:动态日程提醒任务调度)

本文介绍如何在SpringBoot中实现根据数据库动态时间点执行任务的需求,通过自定义任务调度器和装载任务,实现任务在规定时间点执行,支持任务的动态添加和删除,适用于动态日程提醒等场景。
摘要由CSDN通过智能技术生成


1. 需求产生背景

在项目开发中过程中,遇到了一个根据用户动态添加的日程开始时间,在该时间来临前的前半个小时给予消息提醒的需求

第一时间就想到了定时任务调度,纠结了很久最后得出这套解决方案
总共经历了三个阶段,前俩个阶段在网上是有很多资源的,最后一个阶段是在参考了某位大神之后,自己构建出来的,如下:

阶段一:固定重复定时任务
我们知道 Spring 中有一个 @Scheduled 注解,可以使用 cron 表达式实现任务定时重复执行(如每天零点执行一次),但这种方式的缺点也很明显,那就是直接在程序中写死,想要更改必须重启程序

阶段二:动态重复定时任务
而如果能够根据数据库中定义的 cron 表达式,动态改变重复执行的规则,就灵活很多了,虽然这种方法还满足不了我们的需求,但却实实在在地为下一个阶段做好了铺垫

阶段三:动态时间点定时任务
在一些情况下(如本文的需求),任务并不是定时重复执行的,而是由用户或系统生成一个一个附带时间点的任务,希望程序在规定的时间点时执行相应的任务
感谢大神:springboot动态增加删除定时任务


2. 实现思路

在阶段二中,我们可以通过修改数据库中的 cron 表达式,完成定时任务规则的修改
cron 表达式与时间点是可以互相转化的(周和月只能同时存在一个,取月而周不确定)
2022-1-31 09:30:00 可以转化为 00 30 09 31 1 ?(秒 分 时 天 月 周)
这样下来就实现了通过读取时间点来执行任务了

但由于没有书写重复执行,故该表达式执行完一次定时任务后,就不会再执行了
这时候就需要获取下一个最近的时间点,解析成 cron 表达式,来完成按照时间点序列先后执行定时任务

下一个问题是:时间点全部执行完了,但可能会有新的时间点加入
这时候就需要动态监听数据库的变化了,这里产生了两种思路:
① 使用 AOP,在插入新记录时,刷新定时任务
② 在没有任务时,将 cron 设置为定时重复执行,不断轮询数据库,直到有新数据出现就更换 cron 规则

思路一比思路二更通用且节省资源,但思路二更容易实现,且能检测到我们手动往数据库中添加的数据,这里采用思路二

所以具体的实现思路就是:

  • 在 springBoot 项目启动时,为每一个用户分配一个定时任务
  • 在每一个定时任务中,读取出尚未发送通知的时间点最近的一条数据
  • 解析其时间点为 cron 表达式,作为下一次定时任务执行的时间
  • 执行完定时任务后,将该记录标识为已通知
  • 然后继续读取下一条数据并解析为 cron 表达式和执行任务
  • 直到没有任务了,进行轮询,有新数据就解析为新 cron 表达式
  • 有新用户注册时,为新用户动态分配一个定时任务

3. 具体实现(实战)

① 示范建表(只想看逻辑的可以跳过)

实现上述逻辑有很多建表的方式,我这里这个只作为示例拉~

-- 定时任务表
CREATE TABLE t_task (
    task_id      INT AUTO_INCREMENT COMMENT'任务 id' PRIMARY KEY,
    group_id     INT DEFAULT 1      COMMENT'任务所属组别,未指定时为默认组别',
    task_code    INT                COMMENT'任务代码',
    task_time    CHAR(19)           COMMENT'任务执行时间',
    task_run     INT DEFAULT 0      COMMENT'任务执行标识',
    create_date  TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT'任务创建时间'
);

-- 定时任务分组
CREATE TABLE t_group (
    group_id    INT AUTO_INCREMENT COMMENT '组别 id' PRIMARY KEY,
    group_name  VARCHAR(255)       COMMENT '组别名称',
    group_task  INT DEFAULT 0      COMMENT '组别任务数',
    create_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT'组别创建时间'
);

-- 默认组别
INSERT INTO t_group(group_name) VALUE('defaultGroup');

这里建立了两张表,一张任务表和一张任务分组表
一个任务分组对应了一个调度任务,一组内可以有多个任务,各参数解析如下:

  • task_code 指定该定时任务具体需要执行哪个任务,即 Runnable 中的具体逻辑
  • task_time 指定了任务将在什么时间点执行,即在 Trigger 中用它解析成 cron 表达式
  • task_run 标识了任务的执行状态,状态的含义可以自定义,这里定义的是 0 为未执行,-1 为执行完毕,大于 0 的所有值可以标识任务的各种状态(复杂任务需要),这里就具体需求啦,一般 0 和 -1 两个状态已经够用了

用来测试的数据如下:

② 自定义任务调度器(重 - 参考与改造)

这里主要用到 add 和 remove 方法,增加和移除调度任务,规则由外部传入

具体的实现参照了前边那位大神(基本上全搬过来拉)
本质上和阶段二中使用的 taskRegistrar.addTriggerTask 是一样的,不过阶段二中这个没有提供移除任务的接口,所以该类拆开了一层封装,将实际执行任务的 SheduledFuture 提取出来了
往后我们只需要对其构成的集合进行增删,就可以实现任务的动态增加和移除了

@Component
public class MyScheduling implements SchedulingConfigurer {
   

    /** 定时任务注册器 */
    private ScheduledTaskRegistrar taskRegistrar;
    private Set<ScheduledFuture<?>> scheduledFutures = null;
    /** 存放所有定时任务 */
    private Map<
  • 10
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寒冰小澈IceClean

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值