你知道怎么实现定时任务吗?

这篇具有很好参考价值的文章主要介绍了你知道怎么实现定时任务吗?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

诸位读者都知道笔者写东西都是用到才写,笔者的学习足迹自从参加工作之后就是 非系统 学习了,公司里源代码只要有笔者不知道的技术细节,笔者就会仔细的研究清楚,笔者是不喜欢给自己留下问题的那种学习习惯。

为何要写

笔者最近负责了消息发送的一些业务需求,由于笔者工作年限不到一年,且笔者目前只是普通本科大四学生,技术栈并不是很完善(crud程序员只是起点),例如国内很多公司都在用的许雪里大神开发的xxl-job计划任务框架,tk-mybaytis框架(笔者也不知道公司为什么不直接用mybatis而是选用了它的其他版本),自封装的ORM框架(用法与mybatis不一样的地方在于它需要自己写Sql语句进行封装),以及各种自封装的自研框架。由于接触了需求,定时任务发送消息提醒用户。所以笔者不得不学习测试一下定时任务框架来加深更多的了解,不能在开发过程中知其然不知其所以然。

定时任务的实现方式

说到头,定时任务就是类似于将cron语句: 秒 分 时 日 月 周 年  交给linux系统进行执行,到了指定的时间就得执行这些命令,在业务需求里,我们需要系统去定时催促实现某些事情,其实这种场景非常多,要写程序实现计时的功能我觉得读者们应该都有自己的方式:例如 利用java线程sleep的方式,利用 timerTak的工具包,利用redis键值对过期消息回调,利用前端计时器,利用spring框架自带的注解@Scheduled (计划的)搭配cron语句,利用数据库event实现,利用xxl-job框架实现,利用quartz框架实现等等......

之前笔者就分享了一套用java线程池实现的定时任务工具,底层就是sleep方法,缺陷太多,不适合生产环境,所以为了加深对公司项目中xxl-job业务代码的理解,笔者研究了quartz框架实现计划任务的原理与实现方式,记录下来供笔者日后进一步深研。

下面笔者举例部分代码供读者阅读:

1、java线程sleep实现计时任务:

你知道怎么实现定时任务吗?

 2、java工具包TimerTask实现定时任务:

你知道怎么实现定时任务吗?

 3、利用redis键值对过期回调:

你知道怎么实现定时任务吗?

你知道怎么实现定时任务吗?

4、前端js函数setTimeOut(函数,毫秒)也可定时;

5、 spring注解@Scheduled实现定时任务:

你知道怎么实现定时任务吗?

你知道怎么实现定时任务吗?

6、利用数据库事件event实现:

事件调度器: Event Scheduler可以用做定时执行某些特定任务(例如:删除记录、数据统计报告、数据备份等等),来取代原先只能由操作系统的计划任务来执行的工作(可以精确到秒)区分表级别的触发器Tigger(事件触发的任务是周期时间决定,而触发器是由表级动作决定的)。

(1)开启数据库事件调度:

SHOW VARIABLES LIKE 'event%';  表中的event_scheduler值为on即为已经开启。

开启:SET GLOBAL event_scheduler = 1;SET GLOBAL event_scheduler = ON;
关闭:SET GLOBAL event_scheduler = 0;SET GLOBAL event_scheduler = OFF;

如果想持久化配置项可以将event_scheduler=1写入到数据库配置文件my.cnf文件(区别my.inf文件)中。

(2)创建事件调度:

CREATE EVENT 【调度事件名称】
ON SCHEDULE 【指定好调度时间】
DO 【SQL可执行语句】

具体细节以及其他语法读者们请自行www.baidu,com;

(3)举例实现调度语句:

CREATE EVENT delete_db_and_run
ON SCHEDULE  AT  TIMESTEP '2023-04-16 16:00:00'
DO  DELETE FROM alldata

以上就是在2023年4月16日16点整删除数据表alldata的定时计划语法

Quartz框架的技术细节与底层原理

笔者总结的技术点若有任何问题,欢迎读者指出,阅即改正。

技术细节:Quartz是通过数据库持久化来实现计划的数据保留,但它的原理并不是基于数据库event的方式进行实现的计划任务。它可以扩展更多的需求也业务,你可以自定义事件只要它们继承了Job接口。Quartz优质的地方在于它与xxl-job一样是基于数据库进行事件调度的,这样就算是server宕机了也不影响我们定时任务的存储,只要在容忍时间内重启服务器我们的定时任务还是可以正常执行的。

底层原理:我们只需要搞清楚两个问题即可明白该框架的主流程脉络,至于其他的枝叶逻辑笔者并没有深究,这两个主流程问题就是该框架是如何将任务存入数据库的?该框架是如何获取定时任务的时间并判断给出动作的?

问题1:quartz框架是如何将任务存储进它的配置数据表中的?

笔者还是以源码跟踪的方式与读者们一起研究这里面的主要脉络:

首先在官方demo中我们能发现至关重要的语句,也是查找源码的起始点:

你知道怎么实现定时任务吗?

 org.quartz.Scheduler类实现的调度任务方法,就是添加计划任务的主要实现代码,笔者点进源码发现以下方法链调用:

你知道怎么实现定时任务吗? 你知道怎么实现定时任务吗?

你知道怎么实现定时任务吗?

你知道怎么实现定时任务吗?

你知道怎么实现定时任务吗?

你知道怎么实现定时任务吗?

 (触发器同理也能找到类似于更新与插入方法的调用,我们先揪着一个走就行)

你知道怎么实现定时任务吗?

你知道怎么实现定时任务吗?

 这里其实已经很清晰了,运用了JDBC采用动态注入的方式进行数据库的更新与插入操作。

其实quartz还有其他数据库类型可以使用,但是入库的主流程脉络大差不差,我们首先要知道它就是通过我们crud程序员最擅长的方式进行的数据库存储定时任务信息的。

问题2:quartz是如何获取定时任务的时间并判断给出动作的?

关于这个问题,笔者查看了依赖包的层级结构,找到了core中的核心QuartzSchedulerThread类;

你知道怎么实现定时任务吗? 显然这就是quartz做出动作的核心线程类,关于它做了什么,我们只需要看线程的run方法即可:

由于此类的run方法行数比较长,笔者将分开叙述它做了什么:

首先在while中优先给出了同步持有对象锁sigLock,通先查询halted的原子布尔型变量的判断,如果停止的值为false则进入等待阶段等到之后进行下一次扫描:

你知道怎么实现定时任务吗?

 下一步判断线程池可用线程数,可用数大于0后获取将要触发的触发器链表Tiggers:

你知道怎么实现定时任务吗?

 注意方法 this.qsRsrcs.getJobStore().acquireNextTriggers 是获取将要触发的触发器列表:

这里笔者直接将获取将要触发的触发器列表方法核心逻辑展示出来供读者阅读: 

你知道怎么实现定时任务吗?

之后就是 判断时间有没有到该触发的时间,因为触发器列表中的触发器都是将要触发的,判断到快要触发了。所以quartz这里做了等待,如果trigger的nextFireTime比当前时间大2ms则循环等待。timeUntilTrigger就是nextFireTime和当前时间之间的差值,它在循环中不断更新,直到它的值非常小了,之后才继续向下到真正的触发代码:

你知道怎么实现定时任务吗?

 然后就是触发了:

你知道怎么实现定时任务吗?

 我们继续看触发方法tiggersFired()的部分核心代码:

你知道怎么实现定时任务吗?

 之后的逻辑方法就是数据库操作,记录操作:

你知道怎么实现定时任务吗?

 将库修改之后就要进行脚本的执行了(这里需要注意的是执行是线程的异步操作,并行执行,但也有一定的控制逻辑),根据数据库的触发结果获取脚本shell:

你知道怎么实现定时任务吗?

 拿到shell之后就可以到线程池中丢过去执行了:

你知道怎么实现定时任务吗?

你知道怎么实现定时任务吗?你知道怎么实现定时任务吗?

 实现了Runnable接口的shell当然可以直接执行了,最后我们发现它对于未获取到将要触发的触发器列表时,它会自动等待:

你知道怎么实现定时任务吗?

 开始获取锁等待是为了定期执行,后面的获取锁等待是为了在等最近可以触发的触发器列表。

到这里笔者和读者们大概也明白了quartz的获取判断与触发逻辑,理解了第二大主流程脉络,当然里面的逻辑肯定不止这些,但是笔者认为理解了主流程脉络结合实践加深吃入程度即可。

 Quartz框架的实现demo实践

笔者也做了一个小demo实现我们的计划任务业务需求,当然仅仅供读者们阅读与学习,不支持生产使用。

依赖:

        <!-- quartz依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
            <version>2.5.4</version>
        </dependency>

quartz.sql

#1 保存已经触发的触发器状态信息
DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
#2 存放暂停掉的触发器表表
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
#3 调度器状态表
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
#4 存储程序的悲观锁的信息(假如使用了悲观锁)
DROP TABLE IF EXISTS QRTZ_LOCKS;
#5 简单的触发器表
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
#6 存储两种类型的触发器表
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
#7 定时触发器表
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
#8 以blob 类型存储的触发器
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
#9 触发器表
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
#10 job 详细信息表
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
#11 日历信息表
DROP TABLE IF EXISTS QRTZ_CALENDARS;

#job 详细信息表
CREATE TABLE QRTZ_JOB_DETAILS
(
    SCHED_NAME VARCHAR(120) NOT NULL,
    JOB_NAME  VARCHAR(200) NOT NULL,
    JOB_GROUP VARCHAR(200) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    JOB_CLASS_NAME   VARCHAR(250) NOT NULL,
    IS_DURABLE VARCHAR(1) NOT NULL,
    IS_NONCONCURRENT VARCHAR(1) NOT NULL,
    IS_UPDATE_DATA VARCHAR(1) NOT NULL,
    REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
);

CREATE TABLE QRTZ_TRIGGERS
(
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    JOB_NAME  VARCHAR(200) NOT NULL,
    JOB_GROUP VARCHAR(200) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    NEXT_FIRE_TIME BIGINT(13) NULL,
    PREV_FIRE_TIME BIGINT(13) NULL,
    PRIORITY INTEGER NULL,
    TRIGGER_STATE VARCHAR(16) NOT NULL,
    TRIGGER_TYPE VARCHAR(8) NOT NULL,
    START_TIME BIGINT(13) NOT NULL,
    END_TIME BIGINT(13) NULL,
    CALENDAR_NAME VARCHAR(200) NULL,
    MISFIRE_INSTR SMALLINT(2) NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
        REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)
);


CREATE TABLE QRTZ_SIMPLE_TRIGGERS
(
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    REPEAT_COUNT BIGINT(7) NOT NULL,
    REPEAT_INTERVAL BIGINT(12) NOT NULL,
    TIMES_TRIGGERED BIGINT(10) NOT NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);


CREATE TABLE QRTZ_CRON_TRIGGERS
(
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    CRON_EXPRESSION VARCHAR(200) NOT NULL,
    TIME_ZONE_ID VARCHAR(80),
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);


CREATE TABLE QRTZ_SIMPROP_TRIGGERS
(
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    STR_PROP_1 VARCHAR(512) NULL,
    STR_PROP_2 VARCHAR(512) NULL,
    STR_PROP_3 VARCHAR(512) NULL,
    INT_PROP_1 INT NULL,
    INT_PROP_2 INT NULL,
    LONG_PROP_1 BIGINT NULL,
    LONG_PROP_2 BIGINT NULL,
    DEC_PROP_1 NUMERIC(13,4) NULL,
    DEC_PROP_2 NUMERIC(13,4) NULL,
    BOOL_PROP_1 VARCHAR(1) NULL,
    BOOL_PROP_2 VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);


CREATE TABLE QRTZ_BLOB_TRIGGERS
(
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    BLOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);


CREATE TABLE QRTZ_CALENDARS
(
    SCHED_NAME VARCHAR(120) NOT NULL,
    CALENDAR_NAME  VARCHAR(200) NOT NULL,
    CALENDAR BLOB NOT NULL,
    PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
);


CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
(
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_GROUP  VARCHAR(200) NOT NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_FIRED_TRIGGERS
(
    SCHED_NAME VARCHAR(120) NOT NULL,
    ENTRY_ID VARCHAR(95) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    INSTANCE_NAME VARCHAR(200) NOT NULL,
    FIRED_TIME BIGINT(13) NOT NULL,
    SCHED_TIME BIGINT(13) NOT NULL,
    PRIORITY INTEGER NOT NULL,
    STATE VARCHAR(16) NOT NULL,
    JOB_NAME VARCHAR(200) NULL,
    JOB_GROUP VARCHAR(200) NULL,
    IS_NONCONCURRENT VARCHAR(1) NULL,
    REQUESTS_RECOVERY VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,ENTRY_ID)
);


CREATE TABLE QRTZ_SCHEDULER_STATE
(
    SCHED_NAME VARCHAR(120) NOT NULL,
    INSTANCE_NAME VARCHAR(200) NOT NULL,
    LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
    CHECKIN_INTERVAL BIGINT(13) NOT NULL,
    PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
);

CREATE TABLE QRTZ_LOCKS
(
    SCHED_NAME VARCHAR(120) NOT NULL,
    LOCK_NAME  VARCHAR(40) NOT NULL,
    PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);

配置文件application.yml

server:
  port: 9000
spring:
  application:
    name: quartz-service
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://localhost:3306/your quartz db ?useUnicode=true&characterEncoding=utf8
    username: your db username
    password: your db password
    driver-class-name: com.mysql.cj.jdbc.Driver
  quartz:
    # 相关属性配置
    properties:
      org:
        quartz:
          # 数据源
          dataSource:
            globalJobDataSource:
              # URL必须大写
              URL: jdbc:mysql://127.0.0.1:3306/your quartz db ?useUnicode=true&characterEncoding=utf-8&useSSL=false
              driver: com.mysql.cj.jdbc.Driver
              maxConnections: 5
              username: your db username
              password: your db password 
              # 必须指定数据源类型
              provider: hikaricp
          scheduler:
            instanceName: globalScheduler
            # 实例id
            #instanceId: AUTO
            type: com.alibaba.druid.pool.DruidDataSource
          jobStore:
            # 数据源
            dataSource: globalJobDataSource
            # JobStoreTX将用于独立环境,提交和回滚都将由这个类处理
            class: org.quartz.impl.jdbcjobstore.JobStoreTX
            # 驱动配置
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
            # 表前缀
            tablePrefix: QRTZ_
          # 线程池配置
          threadPool:
            class: org.quartz.simpl.SimpleThreadPool
            # 线程数
            threadCount: 10
            # 优先级
            threadPriority: 5

实体类

@Data
public class JobInfo {
    private String jobName;
    private String jobGroup;
    private String triggerName;
    private String triggerGroup;
    private String cron;
    private String className;
    private String status;
    private String nextTime;
    private String prevTime;
    private String config;
}
@Data
public class Message {
    private String title;
    private String message;
    private String jobGroup;
    private String jobName;
    private String sendUserName;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private String sendDate;
}

 核心任务处理类

@Component
public class JobHandler {
    @Resource
    private Scheduler scheduler;
    
    public void addJob(JobInfo jobInfo) throws SchedulerException, ClassNotFoundException {
        Assert.notNull(jobInfo, LocalDateTime.now().toString() + "-> 任务信息为null拒绝生成定时任务");
        JobKey jobKey = JobKey.jobKey(jobInfo.getJobName(), jobInfo.getJobGroup());
        if (!scheduler.checkExists(jobKey)) {
            Class<Job> jobClass = (Class<Job>) Class.forName(jobInfo.getClassName());
            JobDetail jobDetail = JobBuilder.newJob(jobClass)
                    .withIdentity(jobKey)
                    .withIdentity(jobInfo.getJobName(), jobInfo.getJobGroup())
                    .withIdentity(jobInfo.getJobName())
                    .build();
            jobDetail.getJobDataMap().put("config", jobInfo.getConfig());
            TriggerKey triggerKey = TriggerKey.triggerKey(jobInfo.getTriggerName(), jobInfo.getTriggerGroup());
            Trigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity(triggerKey)
                    .withSchedule(CronScheduleBuilder.cronSchedule(jobInfo.getCron()))
                    .build();
            scheduler.scheduleJob(jobDetail,trigger);
        } else {
            throw new SchedulerException(jobInfo.getJobName() + "->定时任务计划库已存在");
        }
    }

    public void pauseJob(String jobGroup, String jobName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        if (scheduler.checkExists(jobKey)) {
            scheduler.pauseJob(jobKey);
        }
    }

    public void resumeJob(String jobGroup, String jobName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        if (scheduler.checkExists(jobKey)) {
            scheduler.resumeJob(jobKey);
        }
    }

    public Boolean deleteJob(String jobGroup, String jobName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        if (scheduler.checkExists(jobKey)) {
            return scheduler.deleteJob(jobKey);
        }
        return false;
    }
    
    public JobInfo getJobInfo(String jobGroup, String jobName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName,jobGroup);
        if (!scheduler.checkExists(jobKey)){
            return null;
        }
        List<? extends Trigger> triggersOfJob = scheduler.getTriggersOfJob(jobKey);
        Assert.notNull(triggersOfJob, LocalDateTime.now().toString()+"->触发器信息为空->"+jobGroup+"/"+jobName);
        TriggerKey key = triggersOfJob.get(0).getKey();
        Trigger.TriggerState state = scheduler.getTriggerState(key);
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        JobInfo jobInfo = new JobInfo();
        jobInfo.setJobGroup(jobGroup);
        jobInfo.setJobName(jobName);
        jobInfo.setTriggerGroup(key.getGroup());
        jobInfo.setJobName(key.getName());
        jobInfo.setClassName(jobDetail.getJobClass().getName());
        jobInfo.setStatus(state.toString());
        if (Objects.nonNull(jobDetail.getJobDataMap())){
            jobInfo.setConfig(JSONObject.toJSONString(jobDetail.getJobDataMap()));
        }
        CronTrigger trigger = (CronTrigger) triggersOfJob.get(0);
        jobInfo.setCron(trigger.getCronExpression());
        return jobInfo;
    }
}

事件类型

@Component
@DisallowConcurrentExecution
public class PlanRemindJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("执行计划任务" + jobExecutionContext.getJobDetail().getDescription());
    }
}
@Component
@DisallowConcurrentExecution
public class TimeEventJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("执行定时任务" + jobExecutionContext.getJobDetail().getDescription());
    }
}

事件类型枚举

public enum JobType {
    //计划任务与定时任务
     PLAN_REMIND_MESSAGE ,
     TIME_EVENT_MESSAGE 
}

业务接口

public interface QuartzService {
    Boolean insertMessage(Message message, JobType jobType) throws SchedulerException, ClassNotFoundException;
    JobInfo getJob(Message message);
    Boolean deleteJob(Message message);
    Boolean resumeJob(Message message);
}

 业务实现类

@Service("QuartzService")
public class QuartzServiceImpl implements QuartzService {

    @Resource
    private JobHandler jobHandler;

    @Override
    public synchronized Boolean insertMessage(Message message, JobType jobType) {
        JobInfo jobInfo = JobGenerator.getJobInfo(message, jobType);
        try {
            jobHandler.addJob(jobInfo);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public JobInfo getJob(Message message) {
        try {
            return jobHandler.getJobInfo(message.getJobGroup(), message.getJobName());
        } catch (SchedulerException e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public synchronized Boolean deleteJob(Message message) {
        try {
            return jobHandler.deleteJob(message.getJobGroup(), message.getJobName());
        } catch (SchedulerException e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public synchronized Boolean resumeJob(Message message) {
        try {
            jobHandler.resumeJob(message.getJobGroup(), message.getJobName());
            return true;
        } catch (SchedulerException e) {
            e.printStackTrace();
            return false;
        }
    }
}

工具类

cron与日期(“yyyy-MM-dd hh:mm:ss”)转换类

public class CronGenerator {
    public static String geCron(Message message){
        String date = message.getSendDate().toString();
        String yyyy = date.substring(0, 4);
        String MM = date.substring(5, 7);
        String dd = date.substring(8, 10);
        String hh = date.substring(11, 13);
        String mm = date.substring(14, 16);
        String ss = date.substring(17, 19);
        return ss+" "+mm+" "+hh+" "+dd+" "+MM+" "+"? "+yyyy;
    }
}

JobInfo实体转换工具类

public class JobGenerator {
    public static JobInfo getJobInfo(Message message, JobType jobType) {
        String cron = CronGenerator.geCron(message);
        JobInfo jobInfo = new JobInfo();
        jobInfo.setCron(cron);
        if (jobType == JobType.PLAN_REMIND_MESSAGE){
            jobInfo.setClassName("com.hlc.quartzservice.jobType.PlanRemindJob");
            jobInfo.setJobGroup(message.getJobGroup());
            jobInfo.setJobName(message.getJobName());
            jobInfo.setTriggerGroup("PLAN_REMIND_MESSAGE");
            jobInfo.setTriggerName("计划任务触发器");
            jobInfo.setConfig(message.getMessage());
        }else if (jobType == JobType.TIME_EVENT_MESSAGE){
            jobInfo.setClassName("com.hlc.quartzservice.jobType.TimeEventJob");
            jobInfo.setJobGroup(message.getJobGroup());
            jobInfo.setJobName(message.getJobName());
            jobInfo.setTriggerGroup("TIME_EVENT_MESSAGE");
            jobInfo.setTriggerName("定时任务触发器");
            jobInfo.setConfig(message.getMessage());
        }
        return jobInfo;
    }
}

开启应用程序进行测试

@SpringBootApplication
public class QuartzServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(QuartzServiceApplication.class, args);
        
        QuartzService quartzService = SpringContextHolder.getBean(QuartzService.class);
        Message message = new Message();
        message.setMessage("测试");
        message.setSendDate("2023-04-15 14:30:00");
        message.setTitle("消息");
        message.setSendUserName("hlc");
        try {
            quartzService.insertMessage(message, JobType.PLAN_REMIND_MESSAGE);
        } catch (SchedulerException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

静态方法获取bean的获取容器上下文工具类(实现ApplicationContextAware接口)

@Component
@Lazy(value = false)
public class SpringContextHolder implements ApplicationContextAware {
    /**
     * 将上下文静态设置,在初始化组件时就进行静态上下文的覆盖(这个覆盖是将远spring容器的上下文对象引用加到我们预定设置)
     */
    private static ApplicationContext applicationContext = null;

    public static ApplicationContext getApplicationContext() {
        assertContextInjected();
        return applicationContext;
    }

    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) {
        assertContextInjected();
        return (T) applicationContext.getBean(name);
    }

    public static  <T> T getBean(Class<T> beanType) {
        assertContextInjected();
        return applicationContext.getBean(beanType);
    }

    @Override
    public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {
        SpringContextHolder.applicationContext = applicationContext;
    }
    
    public void destroy() {
        applicationContext = null;
    }

    private static void assertContextInjected() {
        Assert.notNull(applicationContext,
                "applicationContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder.");
    }

    public static void pushEvent(ApplicationEvent event){
        assertContextInjected();
        applicationContext.publishEvent(event);
    }

}

还有另外一种方法可以测试,就是查库,看看数据库表中有没有发生数据变化?

给的么详细,确定不点点赞,收藏一下吗?文章来源地址https://www.toymoban.com/news/detail-422125.html

到了这里,关于你知道怎么实现定时任务吗?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 使用shedlock实现分布式定时任务锁【防止task定时任务重复执行】

    第一步:引入shedlock相关依赖 ShedLock还可以使用Mongo,Redis,Hazelcast,ZooKeeper等外部存储进行协调,例如使用redis则引入下面的包 第二步:创建数据库表结构,数据库表的脚本如下: 第三步:添加shedlock配置类 (定时任务防重复执行的配置类) 第四步:在启动类上添加启动注

    2024年02月10日
    浏览(42)
  • springboot---定时任务实现

    任意类中创建一个方法,将该方法用@scheduled注解修饰,然后在项目的主方法上添加@EnableScheduling注解,定时任务就会生效。 但是需要注意的是定时任务不会一开始就执行,会等待设定的时间 1.2.1. cron cron表达式是一个字符串,字符串以5或6个空格隔开,分开共6或7个域,每一个

    2024年02月11日
    浏览(38)
  • SpringBoot 实现定时任务

    定时任务在实际项目开发中很常见,并且定时任务可以在各种场景中应用,通过自动化操作和任务的规模化管理,提高效率、可靠性和工作质量。可以减少手动操作,避免疏忽和错误,并节省时间和人力资源的投入 简单易用: 使用注解驱动的方式,简化了定时任务的配置和

    2024年02月12日
    浏览(56)
  • Java -- 定时任务实现方式

    在Java开发中,定时任务是一种十分常见的功能. 定时任务是在约定时间内执行的一段程序 如每天凌晨24点备份同步数据,又或者电商平台 30 分钟后自动取消未支付的订单,每隔一个小时拉取一次数据等都需要使用到定时器 批量处理数据:批量统计上个月的某个数据。 时间驱

    2024年02月02日
    浏览(38)
  • go 中如何实现定时任务

    定时任务是指按照预定的时间间隔或特定时间点自动执行的计划任务或操作。这些任务通常用于自动化重复性的工作,以减轻人工操作的负担,提高效率。在计算机编程和应用程序开发中,定时任务是一种常见的编程模式,用于周期性地执行某些操作、处理数据或触发事件。

    2024年02月05日
    浏览(81)
  • Spring定时任务+webSocket实现定时给指定用户发送消息

    生命无罪,健康万岁,我是laity。 我曾七次鄙视自己的灵魂: 第一次,当它本可进取时,却故作谦卑; 第二次,当它在空虚时,用爱欲来填充; 第三次,在困难和容易之间,它选择了容易; 第四次,它犯了错,却借由别人也会犯错来宽慰自己; 第五次,它自由软弱,却把

    2024年02月07日
    浏览(51)
  • 任务调度框架-如何实现定时任务+RabbitMQ事务+手动ACK

    比如: 1.每天早上6点定时执行 2.每月最后一个工作日,考勤统计 3.每个月25号信用卡还款 4.会员生日祝福 5.每隔3秒,自动提醒 10分钟的超时订单的自动取消,每隔30秒或1分钟查询一次订单,拿当前的时间上前推10分钟 定时任务,资源会有误差的存在,如果使用定时任务 定时

    2024年02月08日
    浏览(38)
  • 一种轻量级定时任务实现

    现在市面上有各式各样的分布式定时任务,每个都有其独特的特点,我们这边的项目因为一开始使用的是分布式开源调度框架TBSchedule,但是这个框架依赖ZK, 由于ZK的不稳定性和项目老旧无人维护 ,导致我们的定时任务会偶发出现异常,比如:任务停止、任务项丢失、任务不

    2024年02月14日
    浏览(48)
  • C#--使用Quartz实现定时任务

    Quartz.NET是一个开源的作业调度框架,非常适合在平时的工作中,定时轮询数据库同步,定时邮件通知,定时处理数据等。 Quartz.NET允许开发人员根据时间间隔(或天)来调度作业。它实现了作业和触发器的多对多关系,还能把多个作业与不同的触发器关联。整合了Quartz.NET的应

    2024年02月08日
    浏览(39)
  • go 利用channel实现定时任务

    想5秒内结束就注释掉select{} 在linux上后台执行的话,可以这样

    2024年04月15日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包