SpringBoot - 集成Quartz框架之具体步骤(三)_quartz集成-程序员宅基地

技术标签: spring boot  quartz  SpringBoot  

写在前面

本文讲述在基于SpringBoot框架的项目中,如何一步一步的集成Quartz框架,项目使用的是PostgreSQL数据库。

具体步骤

1. 添加依赖
<!-- quartz -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
2. 编写配置

在resource目录下创建servicex-quartz.properties文件:

#============================================================================
# 1. 基本配置
#============================================================================

# 调度标识名, 集群中每一个实例都必须使用相同的名称
org.quartz.scheduler.instanceName = SERVICEX-SCHEDULER-INSTANCE-NAME
# ID设置为自动获取, 每一个实例不能相同
org.quartz.scheduler.instanceId = AUTO

#============================================================================
# 2. 调度器线程池配置
#============================================================================

# 线程池的实现类, 一般使用SimpleThreadPool即可满足需求
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
# 指定线程数无默认值, 至少为1
org.quartz.threadPool.threadCount = 10
# 设置线程的优先级(最大为java.lang.Thread.MAX_PRIORITY 10,最小为Thread.MIN_PRIORITY 1,默认为5)
org.quartz.threadPool.threadPriority = 5


#============================================================================
# 3. 作业存储配置
#============================================================================

# 数据保存方式为数据库持久化
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
# 数据库驱动
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
# 是否加入集群
org.quartz.jobStore.isClustered = true
# 检查集群节点状态的频率, 默认值是 15000(15)
org.quartz.jobStore.clusterCheckinInterval = 15000
org.quartz.jobStore.maxMisfiresToHandleAtATime = 1
org.quartz.jobStore.txIsolationLevelSerializable = true
# 设置调度引擎对触发器超时的忍耐时间 (单位毫秒)
org.quartz.jobStore.misfireThreshold = 12000
# 表的前缀,默认QRTZ_
org.quartz.jobStore.tablePrefix = QRTZ_
3. 建库建表

在官网中找到对于的SQL脚本:

-- Thanks to Patrick Lightbody for submitting this...
--
-- In your Quartz properties file, you'll need to set
-- org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;

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        BOOL         NOT NULL,
  IS_NONCONCURRENT  BOOL         NOT NULL,
  IS_UPDATE_DATA    BOOL         NOT NULL,
  REQUESTS_RECOVERY BOOL         NOT NULL,
  JOB_DATA          BYTEA        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       NULL,
  PREV_FIRE_TIME BIGINT       NULL,
  PRIORITY       INTEGER      NULL,
  TRIGGER_STATE  VARCHAR(16)  NOT NULL,
  TRIGGER_TYPE   VARCHAR(8)   NOT NULL,
  START_TIME     BIGINT       NOT NULL,
  END_TIME       BIGINT       NULL,
  CALENDAR_NAME  VARCHAR(200) NULL,
  MISFIRE_INSTR  SMALLINT     NULL,
  JOB_DATA       BYTEA        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       NOT NULL,
  REPEAT_INTERVAL BIGINT       NOT NULL,
  TIMES_TRIGGERED BIGINT       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(120) 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   BOOL           NULL,
  BOOL_PROP_2   BOOL           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     BYTEA        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      BYTEA        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       NOT NULL,
  SCHED_TIME        BIGINT       NOT NULL,
  PRIORITY          INTEGER      NOT NULL,
  STATE             VARCHAR(16)  NOT NULL,
  JOB_NAME          VARCHAR(200) NULL,
  JOB_GROUP         VARCHAR(200) NULL,
  IS_NONCONCURRENT  BOOL         NULL,
  REQUESTS_RECOVERY BOOL         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       NOT NULL,
  CHECKIN_INTERVAL  BIGINT       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)
);

CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY
  ON QRTZ_JOB_DETAILS (SCHED_NAME, REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_J_GRP
  ON QRTZ_JOB_DETAILS (SCHED_NAME, JOB_GROUP);

CREATE INDEX IDX_QRTZ_T_J
  ON QRTZ_TRIGGERS (SCHED_NAME, JOB_NAME, JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_JG
  ON QRTZ_TRIGGERS (SCHED_NAME, JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_C
  ON QRTZ_TRIGGERS (SCHED_NAME, CALENDAR_NAME);
CREATE INDEX IDX_QRTZ_T_G
  ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_T_STATE
  ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_STATE
  ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP, TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_G_STATE
  ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_GROUP, TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME
  ON QRTZ_TRIGGERS (SCHED_NAME, NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST
  ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_STATE, NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE
  ON QRTZ_TRIGGERS (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE
  ON QRTZ_TRIGGERS (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME, TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP
  ON QRTZ_TRIGGERS (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME, TRIGGER_GROUP, TRIGGER_STATE);

CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME
  ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, INSTANCE_NAME);
CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY
  ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, INSTANCE_NAME, REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_FT_J_G
  ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, JOB_NAME, JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_JG
  ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_T_G
  ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_FT_TG
  ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, TRIGGER_GROUP);
COMMIT;
4. 创建调度器工厂
@Configuration
public class SchedulerConfig {
    

    // 配置文件路径
    private static final String SERVICEX_QUARTZ_CONFIG_PATH = "/servicex-quartz.properties";

    // 控制器工厂类
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) {
    
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        factory.setDataSource(dataSource);
        factory.setQuartzProperties(quartzProperties());
        factory.setStartupDelay(1);
        factory.setApplicationContextSchedulerContextKey("SERVICEX-APPLICATION-CONTEXT-KEY");
        factory.setOverwriteExistingJobs(true);
        factory.setAutoStartup(true);
        return factory;
    }

    @Bean
    @ConditionalOnResource(resources = SERVICEX_QUARTZ_CONFIG_PATH)
    Properties quartzProperties() {
    
        try {
    
            PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
            propertiesFactoryBean.setLocation(new ClassPathResource(SERVICEX_QUARTZ_CONFIG_PATH));
            propertiesFactoryBean.afterPropertiesSet();
            Properties properties = propertiesFactoryBean.getObject();
            return properties;
        } catch (IOException e) {
    
            System.out.println("QUARTZ相关的配置文件不存在.");
            e.printStackTrace();
        }
        return null;
    }
}
5. 封装任务
/**
 * 抽象JOB的封装
 * JOB表示一个任务, 也就是执行的具体的内容, 一个JOB可以被多个TRIGGER关联, 但是一个TRIGGER只能关联一个JOB.
 * @author ROCKY
 * @createTime 2022年07月04日
 */

@Slf4j
public abstract class AbstractQuartzJob implements Job {
    

    // 线程本地变量
    private static ThreadLocal<Date> threadLocal = new ThreadLocal<>();

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
    
        ServicexJob job = new ServicexJob();
        BeanUtils.copyProperties(context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES), job);
        try {
    
            before(context, job);
            if (job != null) {
    
                doExecute(context, job);
            }
            after(context, job, null);
        } catch (Exception e) {
    
            after(context, job, e);
            log.error("任务执行异常:", e);
        }
    }

    // 1. 执行前
    protected void before(JobExecutionContext context, ServicexJob job) {
    
        threadLocal.set(new Date());
    }

    // 2. 执行方法, 实际的子JOB重载该方法
    protected abstract void doExecute(JobExecutionContext context, ServicexJob job) throws Exception;

    // 3. 执行后
    protected void after(JobExecutionContext context, ServicexJob job, Exception e) {
    
        threadLocal.remove();
        // 质检完成后会做一些操作
        job.doSomeThings();
    }

}
6. 具体任务
/**
 * 不支持并发执行任务的JOB(禁止并发执行, 表示Quartz不能并发的执行同一个JOB的定义, 特指一个JOB类的多个实例)
 * @author ROCKY
 * @createTime 2022年07月04日
 */
@Slf4j
@DisallowConcurrentExecution
public class MyJobExecution extends AbstractQuartzJob {
    
    @Override
    protected void doExecute(JobExecutionContext context, ServicexJob job) throws Exception {
    
        // 真正的调用逻辑
        log.info("TO DO SOMETHING."+ job.getCronExpression() + "[" + job.getNextValidTime() + "]");
    }
}
7. 编写调度器服务
/**
 * 调度器服务
 * JobDetail:用于定义作业的实例
 * Trigger: 触发器, 执行给定作业计划的组件实例
 * Scheduler: 与调度程序交互的主要的API
 * CronScheduleBuilder:用于创建一个Scheduler的生成器
 * 何时触发, 通过Trigger来定义, 使用TriggerBuilder进行构建
 * 什么任务, 通过JobDetail来定义, 使用JobBuilder进行构建
 * 执行逻辑, 通过JOB中的doExecute方法的具体实现, 执行具体的内容
 *
 * @author ROCKY
 * @createTime 2022年07月04日
 */
public class ServicexScheduler {
    

    // 1. 创建/新增任务
    public static void register(Scheduler scheduler, ServicexJob job, Class<? extends Job> jobClass) throws SchedulerException {
    

        // 1. 构建任务信息
        String jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        // 2.1 构建JOB-DETAIL
        JobKey jobKey = getJobKey(jobId, jobGroup);
        TriggerKey triggerKey = getTriggerKey(jobId, jobGroup);
        JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobKey).build();
        // 2.2 设置参数
        jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);

        // 3. 构建CRON调度器
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
        try {
    
            cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);
        } catch (Exception e) {
    
            e.printStackTrace();
        }

        // 4. 构建触发器
        // .startAt(), 用于指定开始时间
        CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey)
                .startAt(job.getStartTime())
                .withSchedule(cronScheduleBuilder).build();


        // 5. 判断是否存在该定时任务
        if (scheduler.checkExists(jobKey)) {
    
            // 防止创建时存在数据问题(先移除再创建)
            scheduler.deleteJob(jobKey);
        }
        // 6. 根据JOB-DETAIL和建触发器创建定时任务
        scheduler.scheduleJob(jobDetail, trigger);

        // 7. 如果JOB的状态是暂停的/禁用的, 需要暂定该任务
        if (!job.getStatus().equals(ScheduleConstants.Status.ENABLED.getValue())) {
    
            scheduler.pauseJob(jobKey);
        }

    }

    public static void register(Scheduler scheduler, ServicexJob job) throws SchedulerException {
    
        register(scheduler, job, MyJobExecution.class);
    }

    // 2. 暂停任务
    public static int pause(Scheduler scheduler, ServicexJob job) throws SchedulerException {
    
        String jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        JobKey jobKey = getJobKey(jobId, jobGroup);
        if (StringUtils.isNotEmpty(jobId) && StringUtils.isNotEmpty(jobGroup) && scheduler.checkExists(jobKey)) {
    
            scheduler.pauseJob(jobKey);
            return 1;
        }
        return -1;
    }


    // 3. 恢复任务
    public static int resume(Scheduler scheduler, ServicexJob job) throws SchedulerException {
    
        String jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        JobKey jobKey = getJobKey(jobId, jobGroup);
        if (StringUtils.isNotEmpty(jobId) && StringUtils.isNotEmpty(jobGroup) && scheduler.checkExists(jobKey)) {
    
            scheduler.resumeJob(jobKey);
            return 1;
        }
        return -1;
    }

    // 4. 删除任务
    public static int delete(Scheduler scheduler, ServicexJob job) throws SchedulerException {
    
        String jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        JobKey jobKey = getJobKey(jobId, jobGroup);
        if (StringUtils.isNotEmpty(jobId) && StringUtils.isNotEmpty(jobGroup) && scheduler.checkExists(jobKey)) {
    
            scheduler.deleteJob(jobKey);
            return 1;
        }
        return -1;
    }

    // 5. 运行任务
    public static void run(Scheduler scheduler, ServicexJob job) throws SchedulerException {
    
        String jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        JobDataMap dataMap = new JobDataMap();
        dataMap.put(ScheduleConstants.TASK_PROPERTIES, job);
        scheduler.triggerJob(getJobKey(jobId, jobGroup), dataMap);
    }

    // 6. 更新任务
    public static void update(Scheduler scheduler, ServicexJob job) throws SchedulerException {
    
        String jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        JobKey jobKey = getJobKey(jobId, jobGroup);
        if (StringUtils.isNotEmpty(jobId) && StringUtils.isNotEmpty(jobGroup) && scheduler.checkExists(jobKey)) {
    
            // 移除
            scheduler.deleteJob(jobKey);
        }
        // 新建
        register(scheduler, job);
    }

    // 6. 更新任务
    public static void update(Scheduler scheduler, ServicexJob job, Class<? extends Job> jobClass) throws SchedulerException {
    
        String jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        JobKey jobKey = getJobKey(jobId, jobGroup);

        if (StringUtils.isNotEmpty(jobId) && StringUtils.isNotEmpty(jobGroup) && scheduler.checkExists(jobKey)) {
    
            // 移除
            scheduler.deleteJob(jobKey);
        }
        // 新建
        register(scheduler, job, jobClass);
    }


    // 获取任务的键
    private static JobKey getJobKey(String jobId, String jobGroup) {
    
        return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME_PREFIX + jobId, jobGroup);
    }

    // 获取任务的键
    private static JobKey getJobKey(String taskName, String jobId, String jobGroup) {
    
        String jobName = (StringUtils.isNotEmpty(taskName) ? taskName : ScheduleConstants.TASK_CLASS_NAME_PREFIX) + jobId;
        return JobKey.jobKey(jobName, jobGroup);
    }

    // 构建任务触发器
    private static TriggerKey getTriggerKey(String jobId, String jobGroup) {
    
        return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME_PREFIX + jobId, jobGroup);
    }

    // 设置定时任务执行计划的失火策略
    private static CronScheduleBuilder handleCronScheduleMisfirePolicy(ServicexJob job, CronScheduleBuilder cb)
            throws Exception {
    
        switch (job.getMisfirePolicy()) {
    
            case ScheduleConstants.MISFIRE_DEFAULT:
                return cb;
            case ScheduleConstants.MISFIRE_IGNORE_MISFIRES:
                return cb.withMisfireHandlingInstructionIgnoreMisfires();
            case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED:
                return cb.withMisfireHandlingInstructionFireAndProceed();
            case ScheduleConstants.MISFIRE_DO_NOTHING:
                return cb.withMisfireHandlingInstructionDoNothing();
            default:
                return null;
        }
    }
}
8. 注册任务

在服务启动时注册所有的任务:

@Service
@Slf4j
public class ServicexQuartzServiceImpl implements IServicexQuartzService {
    

    @Autowired
    private ServicexQuartzMapperservicexQuartzMapper;
    
    @Autowired
    private Scheduler scheduler;

    @PostConstruct
    public void init() throws SchedulerException {
    
        scheduler.clear();
        List<ServicexJob> jobs = new LambdaQueryChainWrapper<>(servicexQuartzMapper).eq(ServicexJob::getRunType, RunType.AUTO.getValue()).list();
        for (ServicexJob job : jobs) {
    
            ServicexScheduler.register(scheduler, job);
        }
    }
    ...
}

其他文章

SpringBoot - 集成Quartz框架之CRON表达式
SpringBoot - 集成Quartz框架之Quartz简介(一)
SpringBoot - 集成Quartz框架之常用配置(二)
SpringBoot - 集成Quartz框架之具体步骤(三)
SpringBoot - 集成Quartz框架之独立数据源(四)
SpringBoot - 集成Quartz框架之常见问题(五)
SpringBoot - 集成Quartz框架之@DisallowConcurrentExecution注解详解(六)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/goodjava2007/article/details/126982771

智能推荐

攻防世界_难度8_happy_puzzle_攻防世界困难模式攻略图文-程序员宅基地

文章浏览阅读645次。这个肯定是末尾的IDAT了,因为IDAT必须要满了才会开始一下个IDAT,这个明显就是末尾的IDAT了。,对应下面的create_head()代码。,对应下面的create_tail()代码。不要考虑爆破,我已经试了一下,太多情况了。题目来源:UNCTF。_攻防世界困难模式攻略图文

达梦数据库的导出(备份)、导入_达梦数据库导入导出-程序员宅基地

文章浏览阅读2.9k次,点赞3次,收藏10次。偶尔会用到,记录、分享。1. 数据库导出1.1 切换到dmdba用户su - dmdba1.2 进入达梦数据库安装路径的bin目录,执行导库操作  导出语句:./dexp cwy_init/[email protected]:5236 file=cwy_init.dmp log=cwy_init_exp.log 注释:   cwy_init/init_123..._达梦数据库导入导出

js引入kindeditor富文本编辑器的使用_kindeditor.js-程序员宅基地

文章浏览阅读1.9k次。1. 在官网上下载KindEditor文件,可以删掉不需要要到的jsp,asp,asp.net和php文件夹。接着把文件夹放到项目文件目录下。2. 修改html文件,在页面引入js文件:<script type="text/javascript" src="./kindeditor/kindeditor-all.js"></script><script type="text/javascript" src="./kindeditor/lang/zh-CN.js"_kindeditor.js

STM32学习过程记录11——基于STM32G431CBU6硬件SPI+DMA的高效WS2812B控制方法-程序员宅基地

文章浏览阅读2.3k次,点赞6次,收藏14次。SPI的详情简介不必赘述。假设我们通过SPI发送0xAA,我们的数据线就会变为10101010,通过修改不同的内容,即可修改SPI中0和1的持续时间。比如0xF0即为前半周期为高电平,后半周期为低电平的状态。在SPI的通信模式中,CPHA配置会影响该实验,下图展示了不同采样位置的SPI时序图[1]。CPOL = 0,CPHA = 1:CLK空闲状态 = 低电平,数据在下降沿采样,并在上升沿移出CPOL = 0,CPHA = 0:CLK空闲状态 = 低电平,数据在上升沿采样,并在下降沿移出。_stm32g431cbu6

计算机网络-数据链路层_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输-程序员宅基地

文章浏览阅读1.2k次,点赞2次,收藏8次。数据链路层习题自测问题1.数据链路(即逻辑链路)与链路(即物理链路)有何区别?“电路接通了”与”数据链路接通了”的区别何在?2.数据链路层中的链路控制包括哪些功能?试讨论数据链路层做成可靠的链路层有哪些优点和缺点。3.网络适配器的作用是什么?网络适配器工作在哪一层?4.数据链路层的三个基本问题(帧定界、透明传输和差错检测)为什么都必须加以解决?5.如果在数据链路层不进行帧定界,会发生什么问题?6.PPP协议的主要特点是什么?为什么PPP不使用帧的编号?PPP适用于什么情况?为什么PPP协议不_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输

软件测试工程师移民加拿大_无证移民,未受过软件工程师的教育(第1部分)-程序员宅基地

文章浏览阅读587次。软件测试工程师移民加拿大 无证移民,未受过软件工程师的教育(第1部分) (Undocumented Immigrant With No Education to Software Engineer(Part 1))Before I start, I want you to please bear with me on the way I write, I have very little gen...

随便推点

Thinkpad X250 secure boot failed 启动失败问题解决_安装完系统提示secureboot failure-程序员宅基地

文章浏览阅读304次。Thinkpad X250笔记本电脑,装的是FreeBSD,进入BIOS修改虚拟化配置(其后可能是误设置了安全开机),保存退出后系统无法启动,显示:secure boot failed ,把自己惊出一身冷汗,因为这台笔记本刚好还没开始做备份.....根据错误提示,到bios里面去找相关配置,在Security里面找到了Secure Boot选项,发现果然被设置为Enabled,将其修改为Disabled ,再开机,终于正常启动了。_安装完系统提示secureboot failure

C++如何做字符串分割(5种方法)_c++ 字符串分割-程序员宅基地

文章浏览阅读10w+次,点赞93次,收藏352次。1、用strtok函数进行字符串分割原型: char *strtok(char *str, const char *delim);功能:分解字符串为一组字符串。参数说明:str为要分解的字符串,delim为分隔符字符串。返回值:从str开头开始的一个个被分割的串。当没有被分割的串时则返回NULL。其它:strtok函数线程不安全,可以使用strtok_r替代。示例://借助strtok实现split#include <string.h>#include <stdio.h&_c++ 字符串分割

2013第四届蓝桥杯 C/C++本科A组 真题答案解析_2013年第四届c a组蓝桥杯省赛真题解答-程序员宅基地

文章浏览阅读2.3k次。1 .高斯日记 大数学家高斯有个好习惯:无论如何都要记日记。他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第几天。这或许也是个好习惯,它时时刻刻提醒着主人:日子又过去一天,还有多少时光可以用于浪费呢?高斯出生于:1777年4月30日。在高斯发现的一个重要定理的日记_2013年第四届c a组蓝桥杯省赛真题解答

基于供需算法优化的核极限学习机(KELM)分类算法-程序员宅基地

文章浏览阅读851次,点赞17次,收藏22次。摘要:本文利用供需算法对核极限学习机(KELM)进行优化,并用于分类。

metasploitable2渗透测试_metasploitable2怎么进入-程序员宅基地

文章浏览阅读1.1k次。一、系统弱密码登录1、在kali上执行命令行telnet 192.168.26.1292、Login和password都输入msfadmin3、登录成功,进入系统4、测试如下:二、MySQL弱密码登录:1、在kali上执行mysql –h 192.168.26.129 –u root2、登录成功,进入MySQL系统3、测试效果:三、PostgreSQL弱密码登录1、在Kali上执行psql -h 192.168.26.129 –U post..._metasploitable2怎么进入

Python学习之路:从入门到精通的指南_python人工智能开发从入门到精通pdf-程序员宅基地

文章浏览阅读257次。本文将为初学者提供Python学习的详细指南,从Python的历史、基础语法和数据类型到面向对象编程、模块和库的使用。通过本文,您将能够掌握Python编程的核心概念,为今后的编程学习和实践打下坚实基础。_python人工智能开发从入门到精通pdf

推荐文章

热门文章

相关标签