Quartz的简单使用

Quartz

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序

1、快速入门

(1)通过start.spring.io快速构建spring boot工程,加上quartz依赖
Quartz的简单使用

(2)入门程序编写

Quartz的简单使用

public class QuartzTest {
    public static void main(String[] args) {

        try {
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

            scheduler.start();

            // define the job and tie it to our HelloJob class
            // 定义一个需要去执行的job
            JobDetail job = newJob(HelloJob.class)
                    .withIdentity("job1", "group1")
                    .build();
            /**
             * 四种触发器:
             *
             * CalendarIntervalTriggerImpl (org.quartz.impl.triggers)
             * SimpleTriggerImpl (org.quartz.impl.triggers)
             * DailyTimeIntervalTriggerImpl (org.quartz.impl.triggers)
             * CronTriggerImpl (org.quartz.impl.triggers)
             */
            // 创建一个触发器,每隔5秒钟触发执行这个job
            Trigger trigger = newTrigger()
                    .withIdentity("trigger1", "group1")
                    .startNow()
                    .withSchedule(simpleSchedule() // 简单调度
                            .withIntervalInSeconds(5) // 每隔5秒执行一次
                            .repeatForever()) // 永远执行
                    .build();

            // 执行这个job
            scheduler.scheduleJob(job, trigger);
            TimeUnit.SECONDS.sleep(20);
            scheduler.shutdown();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}



public class HelloJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

        System.out.println("hello,quartz " + DateUtil.format(new Date())  + ",当前线程:" + Thread.currentThread().getName());

    }
}

运行结果:
hello,quartz 2021-12-25 10:39:11,当前线程:DefaultQuartzScheduler_Worker-1
hello,quartz 2021-12-25 10:39:16,当前线程:DefaultQuartzScheduler_Worker-2
hello,quartz 2021-12-25 10:39:21,当前线程:DefaultQuartzScheduler_Worker-3
hello,quartz 2021-12-25 10:39:26,当前线程:DefaultQuartzScheduler_Worker-4
hello,quartz 2021-12-25 10:39:31,当前线程:WorkerThread-LastJob

2、触发器

(1)同一个job能够被不同的触发器进行触发(但是一个触发器只能调度一个job)

Quartz的简单使用

public class _01_QuartzTest {
    public static void main(String[] args) {
        try {
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

            scheduler.start();
            // 定义一个需要去执行的job
            JobDetail job = newJob(_01_HelloJob.class)
                    .withIdentity("job1", "group1")
                    .build();

            // 创建一个触发器,每隔1秒钟触发执行这个job
            Trigger trigger1 = newTrigger()
                    .withIdentity("trigger1", "group1")
                    .startNow()
                    .withSchedule(simpleSchedule() // 简单调度
                            .withIntervalInSeconds(1) // 每隔1秒执行一次
                            .repeatForever()) // 永远执行
                    .build();
            
            // 创建第二个触发器,每隔1秒钟触发执行这个job
            Trigger trigger2 = newTrigger()
                    .withIdentity("trigger2", "group1")
                    .forJob("job1","group1") // 第二个触发器  通过jobName和jobGroup名称确定唯一的job
                    .startNow()
                    .withSchedule(simpleSchedule() // 简单调度
                            .withIntervalInSeconds(1) // 每隔1秒执行一次
                            .repeatForever()) // 永远执行
                    .build();
            
            // 执行这个job
            scheduler.scheduleJob(job, trigger1);
            scheduler.scheduleJob(trigger2);  // trigger2已经指定了job
            TimeUnit.SECONDS.sleep(3);
            scheduler.shutdown();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
public class _01_HelloJob implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        StringJoiner std = new StringJoiner("  ")
                .add("hello,quartz")
                .add(DateUtil.format(new Date())) // 执行时候的时间
                .add(Thread.currentThread().getName()) // 当前线程的名称
                .add(jobExecutionContext.getTrigger().getKey().getName()); // 获取trigger名称TriggerKey(String name, String group)
        System.out.println(std);
    }
}
结果;
hello,quartz  2021-12-25 11:10:11  DefaultQuartzScheduler_Worker-1  trigger1
hello,quartz  2021-12-25 11:10:11  DefaultQuartzScheduler_Worker-2  trigger2
hello,quartz  2021-12-25 11:10:12  DefaultQuartzScheduler_Worker-3  trigger1
hello,quartz  2021-12-25 11:10:12  DefaultQuartzScheduler_Worker-4  trigger2
hello,quartz  2021-12-25 11:10:13  DefaultQuartzScheduler_Worker-5  trigger1
hello,quartz  2021-12-25 11:10:13  DefaultQuartzScheduler_Worker-6  trigger2
hello,quartz  2021-12-25 11:10:14  WorkerThread-LastJob  trigger1

(2)同一个job能够被不同的JobDetail引用

quartz模型
Quartz的简单使用

public class _02_QuartzTest {
    public static void main(String[] args) {

        try {
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

            scheduler.start();
            // 定义一个需要去执行的job
            JobDetail job1 = newJob(_02_HelloJob.class) // 同一个job
                    .withIdentity("job1", "group1")
                    .build();

            // 定义一个需要去执行的job
            JobDetail job2 = newJob(_02_HelloJob.class) // 同一个job
                    .withIdentity("job2", "group1")
                    .build();

            // 创建一个触发器,每隔1秒钟触发执行这个job
            Trigger trigger1 = newTrigger()
                    .withIdentity("trigger1", "group1")
                    .startNow()
                    .withSchedule(simpleSchedule() // 简单调度
                            .withIntervalInSeconds(1) // 每隔1秒执行一次
                            .repeatForever()) // 永远执行
                    .build();

            // 创建一个触发器,每隔1秒钟触发执行这个job
            Trigger trigger2 = newTrigger()
                    .withIdentity("trigger2", "group1")
                    .startNow()
                    .withSchedule(simpleSchedule() // 简单调度
                            .withIntervalInSeconds(1) // 每隔1秒执行一次
                            .repeatForever()) // 永远执行
                    .build();

            // 执行这个job
            scheduler.scheduleJob(job1, trigger1);
            scheduler.scheduleJob(job2,trigger2);
            TimeUnit.SECONDS.sleep(3);
            scheduler.shutdown();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
public class _02_HelloJob implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        StringJoiner std = new StringJoiner("  ")
                .add("hello,quartz")
                .add(DateUtil.format(new Date())) // 执行时候的时间
                .add(Thread.currentThread().getName()) // 当前线程的名称
                .add(jobExecutionContext.getTrigger().getKey().getName()) // 获取trigger名称
                .add(jobExecutionContext.getJobDetail().getKey().getName()); // 获取JobDetail名称
        System.out.println(std);
    }
}
结果:
hello,quartz  2021-12-25 11:28:05  DefaultQuartzScheduler_Worker-1  trigger1  job1
hello,quartz  2021-12-25 11:28:05  DefaultQuartzScheduler_Worker-2  trigger2  job2
hello,quartz  2021-12-25 11:28:06  DefaultQuartzScheduler_Worker-3  trigger1  job1
hello,quartz  2021-12-25 11:28:06  DefaultQuartzScheduler_Worker-4  trigger2  job2
hello,quartz  2021-12-25 11:28:07  DefaultQuartzScheduler_Worker-5  trigger1  job1
hello,quartz  2021-12-25 11:28:07  DefaultQuartzScheduler_Worker-6  trigger2  job2
hello,quartz  2021-12-25 11:28:08  DefaultQuartzScheduler_Worker-7  trigger1  job1
hello,quartz  2021-12-25 11:28:08  DefaultQuartzScheduler_Worker-8  trigger2  job2

(3)关于jobDetail和Trigger的name和group

group是用来标记、方便管理的

name是用来标记的

举例:

一个物流系统有三个模块:
运输工具模块
运输人员模块
物流信息模块

job是每天凌晨结算前一天的物流信息

Trigger{group:物流信息,name:日结-auto}
JobDetail{group:物流信息,name:日结}

由于一些原因,需要修改昨天的物流信息,一般会在后台提供手动触发的功能
Trigger{group:物流信息,name:日结-manual}

(4)SimpleTriggerImpl(简单触发器)

(1)可以指定触发的时间间隔
Quartz的简单使用

(2)可以指定触发的次数
Quartz的简单使用

(5)CronTriggerImpl (org.quartz.impl.triggers)

核心方法

CronScheduleBuilder.cronSchedule("* * * * * ? *") // 传入cron表达式

Quartz的简单使用

解释:

Quartz的简单使用

Quartz的简单使用

(6)传入变量

           // 定义一个需要去执行的job
            JobDetail job = newJob(_05_HelloJob.class)
                    .withIdentity("job1", "group1")
                    .usingJobData("JobDetail_key","JobDetail_value")
                    .build();

            // 创建一个触发器,每隔1秒钟触发执行这个job
            Trigger trigger = newTrigger()
                    .withIdentity("trigger", "group1")
                    .usingJobData("trigger_key","trigger_value")
                    .startNow()
                    .withSchedule(
                            CronScheduleBuilder.cronSchedule("* * * * * ? *") // 传入cron表达式
                     )
                    .build();
 @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        //获取传入的变量
        Trigger trigger = jobExecutionContext.getTrigger();
        JobDetail jobDetail = jobExecutionContext.getJobDetail();

        JobDataMap triggerJobDataMap = trigger.getJobDataMap();
        System.out.println("triggerJobDataMap ==> " + triggerJobDataMap.get("trigger_key"));


        //获取传入的变量
        JobDataMap jobDetailJobDataMap = jobDetail.getJobDataMap();
        System.out.println("jobDetailJobDataMap ==> " + jobDetailJobDataMap.get("JobDetail_key"));

        StringJoiner std = new StringJoiner("  ")
                .add("hello,quartz")
                .add(DateUtil.format(new Date())) // 执行时候的时间
                .add(Thread.currentThread().getName()) // 当前线程的名称
                .add(jobExecutionContext.getTrigger().getKey().getName()) // 获取trigger名称
                .add(jobExecutionContext.getJobDetail().getKey().getName()); // 获取JobDetail名称
        System.out.println(std);
    }

3、与spring的集成

定义一个job

public class _01_SpringJob extends QuartzJobBean { //继承QuartzJobBean
    @Autowired
    private SpringAutoService springAutoService;// 注入

    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        StringJoiner std = new StringJoiner("---")
                .add("hello,quartz")
                .add(DateUtil.format(new Date())) // 执行时候的时间
                .add(Thread.currentThread().getName());
        System.out.println(std);
        System.out.println(springAutoService.getHelloSpring());
    }
}
@Service
public class SpringAutoService {
    public String getHelloSpring(){
        return "hello spring";
    }
}

(1)第一种方式

@Component
public class JobInit {
    @Autowired
    public Scheduler scheduler; // 自动注入
    
    @PostConstruct
    public void initJob() throws Exception{
        JobDetail jobDetail = JobBuilder.newJob(_01_SpringJob.class)
                .withIdentity("job1", "group1")
                .build();
        
        Trigger trigger = TriggerBuilder.newTrigger()
               .withIdentity("trigger1", "group1")
                .startNow()
                .withSchedule(simpleSchedule() // 简单调度
                        .withIntervalInSeconds(1) // 每隔1秒执行一次
                        .withRepeatCount(3)
                ).build();
        scheduler.scheduleJob(jobDetail,trigger);
    }
}
启动springboot工程:
*****************QuartzStudyApplication启动成功************************
hello,quartz---2021-12-25 17:35:30---quartzScheduler_Worker-2
hello spring
hello,quartz---2021-12-25 17:35:31---quartzScheduler_Worker-3
hello spring
hello,quartz---2021-12-25 17:35:32---quartzScheduler_Worker-4
hello spring

(2)第二种方式,会自动关联

@Configuration
public class JobConfig {

    @Bean
    public JobDetail getJobDetail(){
        JobDetail jobDetail = JobBuilder.newJob(_01_SpringJob.class)
                .withIdentity("job1", "group1")
                .storeDurably()
                .build();

        return jobDetail;
    }

    @Bean
    public Trigger getTrigger(){
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1")
                .forJob("job1","group1")
                .startNow()
                .withSchedule(simpleSchedule() // 简单调度
                        .withIntervalInSeconds(1) // 每隔1秒执行一次
                        .withRepeatCount(3)
                ).build();
        return trigger;
    }
}
*****************QuartzStudyApplication启动成功************************
hello,quartz---2021-12-25 17:48:46---quartzScheduler_Worker-1
hello spring
hello,quartz---2021-12-25 17:48:47---quartzScheduler_Worker-2
hello spring
hello,quartz---2021-12-25 17:48:48---quartzScheduler_Worker-3
hello spring
hello,quartz---2021-12-25 17:48:49---quartzScheduler_Worker-4
hello spring

4、持久化

配置如下:

(1) quartz的表和业务库在一起

# 配置数据源
spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://127.0.0.1:3306/exercise?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
# quartz持久化 会在exercise中创建quartz表
  quartz:
    job-store-type: jdbc
    jdbc:
      initialize-schema: always // 每次启动会重新创建表  初次启动后可以改为never

(2) quartz的表单独在一个库中

 @Bean
    @QuartzDataSource
    public DataSource dataSource(){
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("");
        dataSource.setUsername("");
        dataSource.setPassword("");
        dataSource.setUrl("");
        return dataSource;
    }
  quartz:
    properties: # 添加原生配置
      org.quartz.threadPool.threadCount: 10
      username: root
      ...

5、集群环境
Quartz的简单使用

(1)只让集群中的一台服务器去跑定时任务(会浪费其他服务器的性能)

(2)每台服务器都会执行定时任务,但是一个任务只会分配到一台机器上

Quartz的简单使用

上一篇:7-6 喷水装置 (25 分)


下一篇:C#使用iTextSharp将图片转为pdf