zoukankan      html  css  js  c++  java
  • 任务调度-Quartz

    我们常用的任务调度有:

    springTask:spring自带的,使用@Scheduled注解就可以简单快速的实现一个任务

    Quartz:是一个非常成熟的任务调度工具,可以精确到毫秒级别, 独立运行,可以集成到容器中, 支持事务(JobStoreCMT ), 支持集群 ,支持持久化

    xxx-job:分布式的任务调度,以前基于Quartz

    Elastic-Job:分布式的任务调度,基于Quartz

    1、基本结构

     依赖:

    <dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.0</version>
    </dependency>

    springboot中

    <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-quartz</artifactId>
            </dependency>

     2、关键角色

    2.1 Job:任务对象,任务执行的内容,我们写的任务需要实现Job接口,context可以获取到JobDetail中携带的信息

    public class MyJob implements Job {
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            System.out.println("触发器信息>>>"+context.getTrigger());
            System.out.println("触发器名字>>>"+context.getTrigger().getKey().getName());
            System.out.println("触发器组名>>>"+context.getTrigger().getKey().getGroup());
            System.out.println("任务创建人>>>"+context.getMergedJobDataMap().get("creater"));
            System.out.println("任务创建时间 >>>"+context.getMergedJobDataMap().get("createTime"));
            System.out.println("任务创建内容 >>>现在时间"+ LocalDateTime.now());
        }
    }

    JobDetail用来包装job,指定jobName和groupName组成的唯一标识,jobDataMap可以携带kv数据,context可以获取信息

    JobDetail jobDetail = JobBuilder.newJob(MyJob.class)
                    //组成唯一标识
                    .withIdentity("jobkey","groupkey")
                    //JobData携带数据,可在context中获取
                    .usingJobData("creater","jack")
                    .usingJobData("createTime","2020/11/4")
                    .build();

    2.2、Trigger:触发器,有不同的规则来触发任务的执行。

    SimpleTrigger:简单触发器, 固定时刻或时间间隔,毫秒

    CalendarIntervalTrigger: 基于日历的触发器,可以根据月份、年份来触发,天数和年数不固定也适用,我们可以不需要去计算时间间隔。

    DailyTimeIntervalTrigger: 基于日期的触发器 每天的某个时间段,每周末哪几天触发

    CronTrigger :基于 Cron 表达式的触发器,全能 ,在线cron生成器 https://qqe2.com/cron

    比如这个simpletrigger,每两秒触发任务

     ScheduleBuilder simpleScheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
                    .withIntervalInSeconds(2)
                    .repeatForever();
    
            Trigger trigger = TriggerBuilder.newTrigger()
              //唯一标识 .withIdentity(
    "simpletrigger1","simpletrigger1")
              //立刻启动,可以指定某个时间 .startNow()
              //可以将上面的直接放在这里面 .withSchedule(simpleScheduleBuilder) .build();

    2.3 Scheduler:调度器,由 StdSchedulerFactory 产生,单例的,可以启动和停止任务,操作trigger和job

       SchedulerFactory schedulerFactory = new StdSchedulerFactory();
            Scheduler scheduler = schedulerFactory.getScheduler();
            scheduler.scheduleJob(jobDetail,trigger);
            scheduler.start();

    2.4 Listener:监听器,Quartz 中提供了三种 Listener,监听 Scheduler 的,监听 Trigger 的,监听 Job 的。

    我们只需要实现相应的接口,然后在调度器中注册,既可以实现功能。

    JobListener:

    public class  implements JobListener {
        @Override
        public String getName() {
            //返回JobListener的名字
            return "JobListenerTest";
        }
    
        @Override
        public void jobToBeExecuted(JobExecutionContext context) {
            System.out.println("任务调用前执行。。。");
        }
    
        @Override
        public void jobExecutionVetoed(JobExecutionContext context) {
            System.out.println("任务调用前,又被TriggerListener否决了执行。。。");
        }
    
        @Override
        public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
            System.out.println("任务调用后执行。。。");
        }
    }

    调度器中注册:

    SchedulerFactory schedulerFactory = new StdSchedulerFactory();
    Scheduler scheduler = schedulerFactory.getScheduler();
    scheduler.scheduleJob(jobDetail,trigger);
    //注册JobListener
    scheduler.getListenerManager().addJobListener(new JobListenerTest());
    scheduler.start();

    执行结果:

     TriggerListener:也是在调度器中注册;

    public class TriggerListenerTest implements TriggerListener {
        @Override
        public String getName() {
            //返回监听器的名称
            return "TriggerListenerTest";
        }
    
        @Override
        public void triggerFired(Trigger trigger, JobExecutionContext context) {
            //Trigger 被触发,Job 上的 execute() 方法将要被执行时,Scheduler 就调用这个
            //方法
            System.out.println("Trigger 被触发");
        }
    
        @Override
        public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
           /* 在 Trigger 触发后 , Job 将被执行前时 由 Scheduler调用这个方 法 。
            TriggerListener 给了一个选择去否决Job 的执行。假如这个方法返回true,这
            个 Job 将不会为此次 Trigger 触发而得到执行,默认为false*/
            return false;
        }
    
        @Override
        public void triggerMisfired(Trigger trigger) {
            //Trigger 错过触发时调用
            System.out.println("Trigger 错过触发时调用");
        }
    
        @Override
        public void triggerComplete(Trigger trigger, JobExecutionContext context, Trigger.CompletedExecutionInstruction triggerInstructionCode) {
            //Trigger 被触发并且完成了 Job 的执行时
            System.out.println("Trigger 被触发并且完成了 Job 的执行");
        }
    }

    任务执行结果:trigger的triggerFired()JobListenerTest的jobToBeExecuted()调用之前执行,

     3、Calendar 排除规则

    如果要在触发器的基础上,排除一些时间区间不执行任务,就要用到 Quartz 的 Calendar 类,可以按年、月、周、日、特定日期、Cron 表达式排除。

     AnnualCalendar:排除年中一天或多天

    WeeklyCalendar:排除一个星期中的周几,例如排除周末,默认周六和周日

    HolidayCalendar:排除节假日,如国家法定节假日

    CronCalendar:排除了由给定的 Cron表达式的时间集合

    DailyCalendar:排除某个时间段,比如排除非9:00-17:00的时间

    MonthlyCalendar:排除月份中的指定数天,例除排除每月的最后一天或第一天

    使用:

    public static void main(String[] args) throws SchedulerException, ParseException {
            JobDetail jobDetail = JobBuilder.newJob(MyJob.class)
                    .usingJobData("creater","suwu")
                    .usingJobData("createTime","2020/11/11")
                    .build();
    
            SimpleScheduleBuilder scheduleBuilder =  SimpleScheduleBuilder.simpleSchedule()
                    .withIntervalInSeconds(2)
                    .repeatForever();
    
          //排除一个星期中的周几,3表示星期二,1表示星期日 WeeklyCalendar weeklyCalendar
    = new WeeklyCalendar(); weeklyCalendar.setDayExcluded(3,true); Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("WeeklyCalendar", "WeeklyCalendar") .startNow() // .startAt(date) .modifiedByCalendar("weekly2") .withSchedule(scheduleBuilder) .build(); SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); scheduler.scheduleJob(jobDetail,trigger); scheduler.addCalendar("weekly2",weeklyCalendar,true,true); scheduler.start(); }

    4、任务储存

    Jobstore 用来存储任务和触发器相关的信息,例如所有任务的名称、数量、状态等等,可以用来做可视化界面的管理

    Quartz 中有两种存储任务的方式,一种在在内存,一种是在数据库,默认是在内存中RAMJobstore,存在内存中

    的坏处就是系统重启后,存储在内存中的数据都会丢失,所以一般采用持久化处理,存在数据库JDBCJobStore中

    1、首先我们需要配置数据库的信息:

    org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
    org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
    # 使用 quartz.properties,不使用默认配置
    org.quartz.jobStore.useProperties:false
    #数据库中 quartz 表的表名前缀
    org.quartz.jobStore.tablePrefix:QRTZ_
    org.quartz.jobStore.dataSource:myQuartz
    #配置数据源
    org.quartz.dataSource.myQuartz.driver:com.mysql.jdbc.Driver
    org.quartz.dataSource.myQuartz.URL:jdbc:mysql://localhost:3306/boot?useUnicode=true&characterEncoding=utf8
    org.quartz.dataSource.myQuartz.user:root
    org.quartz.dataSource.myQuartz.password:123456
    org.quartz.dataSource.myQuartz.validationQuery=select 0 from dual
    org.quartz.threadPool.threadCount: 10
    org.quartz.jobStore.misfireThreshold:10

    这个配置在quartz.properties的文件中,自己创建。

    quartz会默认加载org.quartz下的quartz.properties文件,这里覆盖了就加载我们自己创建的文件,文件需要同名。

     默认是RAMJobStore

    org.quartz.scheduler.instanceName: DefaultQuartzScheduler
    org.quartz.scheduler.rmi.export: false
    org.quartz.scheduler.rmi.proxy: false
    org.quartz.scheduler.wrapJobExecutionInUserTransaction: false
    
    org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
    org.quartz.threadPool.threadCount: 10
    org.quartz.threadPool.threadPriority: 5
    org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
    
    org.quartz.jobStore.misfireThreshold: 60000
    
    org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore

    2、创建表

    quartz已经提供了不同数据库的建表语句

    mysql的建表语句:

    #
    # In your Quartz properties file, you'll need to set
    # org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
    #
    #
    # By: Ron Cordell - roncordell
    #  I didn't see this anywhere, so I thought I'd post it here. This is the script from Quartz to create the tables in a MySQL database, modified to use INNODB instead of MYISAM.
    
    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_SIMPROP_TRIGGERS;
    DROP TABLE IF EXISTS QRTZ_CRON_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(190) NOT NULL,
    JOB_GROUP VARCHAR(190) 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))
    ENGINE=InnoDB;
    
    CREATE TABLE QRTZ_TRIGGERS (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(190) NOT NULL,
    TRIGGER_GROUP VARCHAR(190) NOT NULL,
    JOB_NAME VARCHAR(190) NOT NULL,
    JOB_GROUP VARCHAR(190) 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(190) 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))
    ENGINE=InnoDB;
    
    CREATE TABLE QRTZ_SIMPLE_TRIGGERS (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(190) NOT NULL,
    TRIGGER_GROUP VARCHAR(190) 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))
    ENGINE=InnoDB;
    
    CREATE TABLE QRTZ_CRON_TRIGGERS (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(190) NOT NULL,
    TRIGGER_GROUP VARCHAR(190) 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))
    ENGINE=InnoDB;
    
    CREATE TABLE QRTZ_SIMPROP_TRIGGERS
      (
        SCHED_NAME VARCHAR(120) NOT NULL,
        TRIGGER_NAME VARCHAR(190) NOT NULL,
        TRIGGER_GROUP VARCHAR(190) 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))
    ENGINE=InnoDB;
    
    CREATE TABLE QRTZ_BLOB_TRIGGERS (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(190) NOT NULL,
    TRIGGER_GROUP VARCHAR(190) NOT NULL,
    BLOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
    ENGINE=InnoDB;
    
    CREATE TABLE QRTZ_CALENDARS (
    SCHED_NAME VARCHAR(120) NOT NULL,
    CALENDAR_NAME VARCHAR(190) NOT NULL,
    CALENDAR BLOB NOT NULL,
    PRIMARY KEY (SCHED_NAME,CALENDAR_NAME))
    ENGINE=InnoDB;
    
    CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_GROUP VARCHAR(190) NOT NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))
    ENGINE=InnoDB;
    
    CREATE TABLE QRTZ_FIRED_TRIGGERS (
    SCHED_NAME VARCHAR(120) NOT NULL,
    ENTRY_ID VARCHAR(95) NOT NULL,
    TRIGGER_NAME VARCHAR(190) NOT NULL,
    TRIGGER_GROUP VARCHAR(190) NOT NULL,
    INSTANCE_NAME VARCHAR(190) 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(190) NULL,
    JOB_GROUP VARCHAR(190) NULL,
    IS_NONCONCURRENT VARCHAR(1) NULL,
    REQUESTS_RECOVERY VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,ENTRY_ID))
    ENGINE=InnoDB;
    
    CREATE TABLE QRTZ_SCHEDULER_STATE (
    SCHED_NAME VARCHAR(120) NOT NULL,
    INSTANCE_NAME VARCHAR(190) NOT NULL,
    LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
    CHECKIN_INTERVAL BIGINT(13) NOT NULL,
    PRIMARY KEY (SCHED_NAME,INSTANCE_NAME))
    ENGINE=InnoDB;
    
    CREATE TABLE QRTZ_LOCKS (
    SCHED_NAME VARCHAR(120) NOT NULL,
    LOCK_NAME VARCHAR(40) NOT NULL,
    PRIMARY KEY (SCHED_NAME,LOCK_NAME))
    ENGINE=InnoDB;
    
    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;
    View Code

    3、运行程序,即可在数据库中查询到任务的储存数据

    JDBC 的实现方式有两种,JobStoreSupport 类的两个子类:

    JobStoreTX:在独立的程序中使用,自己管理事务,不参与外部事务。

    JobStoreCMT:如果需要容器管理事 务时,使用它。

    当前项目使用的是JobStoreTX

    Quartz错过触发的处理: https://www.cnblogs.com/daxin/p/3919927.html

  • 相关阅读:
    java面向对象第六章
    java面向对象第四章
    java面向对象第三章
    java面向对象第二章
    java面向对象第一章
    java基础(9)
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    最小堆解决TOP N问题
    kafka相关
    golang 反射获取参数对应类型原始值
  • 原文地址:https://www.cnblogs.com/tdyang/p/13959637.html
Copyright © 2011-2022 走看看