SpringBoot 分布式定时任务-Quartz

简介

Quartz是完全由Java开发的一个开源的作业调度框架。在业务系统中,主要用于做分布式定时任务。Spring本身提供的@Schedule只能实现一些相对简单的单机定时任务,如果跑定时任务的的机器出现故障,则会影响业务系统正常运行。而使用Quartz来开发分布时定时任务,将任务数据存储于数据库中,可以保证任务的单机执行,同时在执行机出现故障,则自动飘到集群中的其它机器执行,不会影响业务的正常进行。

官网:http://www.quartz-scheduler.org/

核心概念

Scheduler: 任务调度器,可以进行调度,暂停和删除任务。

Trigger: 触发器,描述触发任务的时间触发规则,主要使用的有两个子类,分别是:SimpleTriggerCronTrigger,当任务是只需调用一次或是以固定时间间隔周期进行调用执行,使用SimpleTrigger比较适合,当任务是以比较复杂时间规则的调用,则使用CronTrigger比较适合,CronTrigger的时间规则可以通过cron表达式进行定义。

JobDetaila: 定义任务的详情,包括了任务的唯一标识和具体要执行的任务。可通过JobDetailMap往任务中传递数据。

Job: 具体的任务,包括了执行任务的具体方法,是一个接口,需要实现接口的execute()方法。

任务存储类型

RAMJobStore: 即内存存储方式,Quartz默认启用的是内存存储类型,这种方式的性能是最好,但缺点就是缺乏持久性,在程序重启或奔溃的时候,所有的运行信息都会丢失,只能做单点,不能做分布式任务。

JDBC: 即数据库存储方式,将定时任务数据存储到数据库后,可以做单点,也可以做分布式集群,可以对任务进行停止、暂停、修改操作,重启应用后,任务的运行信息不会丟失。

添加依赖

<!-- 添加quartz支持 -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

<!-- 添加mysql 数据库支持 -->
<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
</dependency>

<!-- 添加mybatis-plus支持  -->
<dependency>
   <groupId>com.baomidou</groupId>
   <artifactId>mybatis-plus-boot-starter</artifactId>
   <version>3.5.2</version>
</dependency>

<!-- 添加Druid数据源 -->
<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>druid-spring-boot-starter</artifactId>
   <version>1.2.13-SNSAPSHOT</version>
</dependency>

配置

spring:
  datasource:  # 配置数据库信息
    username: root #用户名
    password: 123456789
    url: jdbc:mysql://localhost:3306/quartz?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver  # mysql连接驱动
    type: com.alibaba.druid.pool.DruidDataSource  # 启用Druid数据源
    druid:
      max-active: 20  # 最大连接池数量
      min-idle: 5  # 最小连接池数量

  quartz:   # 配置quartz 信息
    job-store-type: jdbc # 使用数据库存储定时任务
    jdbc:
      initialize-schema: embedded
    overwrite-existing-jobs: true  # 启动时更新己存在的Job
    properties:
      org:
        quartz:
          jobStore:
            isClustered: true  # 是否以集群方式运行
            useProperties: false  # 如果为true则表示将JobDataMaps中的所有值都作为字符串存储至数据库中

# 配置mybatis-plus信息
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    auto-mapping-behavior: full
  mapper-locations: classpath:mapper/*.xml

用法

定义任务实体类

@Data
public class QuartzBean {
    private String jobName;
    private String groupName;
    private String cron;
    private Class<? extends Job> jobClass;
    private JobDataMap jobDataMap;
}

定义Job类

定义一个定时任务调用执行的类,该类需要继承QuartzJobBean,如下:

// 具体执行业务逻辑的任务类
@DisallowConcurrentExecution
public class DownloadJob extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        // 获取任务的参数
        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
        System.out.println(jobDataMap.get("one"));
        System.out.println("正在执行任务");
      	// TODO 执行业务逻辑
    }
}

任务操作

新建任务

@Service
public class QuartzService {

    @Resource
    private Scheduler scheduler;

    /**
     * 新建 JOB
     * @param quartzBean
     */
    public void createJob(QuartzBean quartzBean) throws SchedulerException {
        // 生成JobKey,针对Job唯一识别
        JobKey jobKey = JobKey.jobKey(quartzBean.getJobName(), quartzBean.getGroupName());
        // 生成JobDetail对象,构建定时任务信息
        JobDetail jobDetail = JobBuilder.newJob(quartzBean.getJobClass()).withIdentity(jobKey).setJobData(quartzBean.getJobDataMap()).build();
        // 构建Cron表达式,即构建定时任务执行方式
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(quartzBean.getCron());
        // 生成TriggerKey,针对Trigger的唯一识别
        TriggerKey triggerKey = TriggerKey.triggerKey(quartzBean.getJobName(), quartzBean.getGroupName());
        // 构建Trigger
        CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();
        // 将Job与Trigger传入到scheduler调度器中
        scheduler.scheduleJob(jobDetail, trigger);
        // 启动调度器
        scheduler.start();
    }
}

一次性任务

@Service
public class QuartzService {

    @Resource
    private Scheduler scheduler;
		
  
  	/**
     * 一次性任务,即只执行一次
     * @param quartzBean
     * @throws SchedulerException
     */
    public void oneWayJob(QuartzBean quartzBean) throws SchedulerException {
        // 生成JobKey,针对Job唯一识别
        JobKey jobKey = JobKey.jobKey(quartzBean.getJobName(), quartzBean.getGroupName());
        // 生成JobDetail对象,构建定时任务信息
        JobDetail jobDetail = JobBuilder.newJob(quartzBean.getJobClass()).withIdentity(jobKey).setJobData(quartzBean.getJobDataMap()).build();
        // 生成TriggerKey,针对Trigger的唯一识别
        TriggerKey triggerKey = TriggerKey.triggerKey(quartzBean.getJobName(), quartzBean.getGroupName());
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(SimpleScheduleBuilder.simpleSchedule().withRepeatCount(0)).build();
        scheduler.scheduleJob(jobDetail, trigger);
        scheduler.start();
    }
}

暂停任务

@Service
public class QuartzService {

    @Resource
    private Scheduler scheduler; 
  	/**
     * 暂停JOB
     * @param quartzBean
    */
    public void pauseJob(QuartzBean quartzBean) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(quartzBean.getJobName(), quartzBean.getGroupName());
        scheduler.pauseJob(jobKey);
    }
}

恢复任务

@Service
public class QuartzService {

    @Resource
    private Scheduler scheduler;   
  
  	/**
     * 恢复JOB
     * @param quartzBean
    */
    public void resumeJob(QuartzBean quartzBean) throws SchedulerException {
        JobKey jobKey  = JobKey.jobKey(quartzBean.getJobName(), quartzBean.getGroupName());
        scheduler.resumeJob(jobKey);
    }
}

删除任务

@Service
public class QuartzService {

    @Resource
    private Scheduler scheduler;   
  
  	/**
     * 删除JOB
     * @param quartzBean
     */
    public void deleteJob(QuartzBean quartzBean) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(quartzBean.getJobName(), quartzBean.getGroupName());
        scheduler.deleteJob(jobKey);
    }
  
}

获取所有任务

@Service
public class QuartzService {

    @Resource
    private Scheduler scheduler;  
  
  	/**
     * 获取所有计划中的任务列表
     * @return
     * @throws SchedulerException
    */
    public List<Map<String, String>> queryAllJob() throws SchedulerException {
        List<Map<String, String>> jobList = null;
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        Set<JobKey> jobKeySet = scheduler.getJobKeys(matcher);
        for (JobKey jobKey : jobKeySet) {
            List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
            List<Map<String, String>> jobs = triggers.stream().map(trigger -> {
                try {
                    Map<String, String> map = new HashMap<>();
                    map.put("jobName", jobKey.getName());
                    map.put("jobGroupName", jobKey.getGroup());
                    Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                    map.put("status", triggerState.name());
                    if(trigger instanceof CronTrigger){
                        CronTrigger cronTrigger = (CronTrigger) trigger;
                        map.put("cron", cronTrigger.getCronExpression());
                    }
                    return map;
                } catch (SchedulerException e) {
                    throw new RuntimeException(e);
                }
            }).collect(Collectors.toList());
            jobList.addAll(jobs);
        }
        return jobList;
    }
}

任务调用

在Controller中调用相关的任务,下面以新增JOB为例,如下:

@RestController
@RequestMapping("task")
public class QuartzController {

    @Autowired
    private QuartzService quartzService;

    @PostMapping
    public String createJob() throws SchedulerException {
        QuartzBean quartzBean = new QuartzBean();
        quartzBean.setCron("*/5 * * * * ?");
        quartzBean.setJobName("TestJob");
        quartzBean.setGroupName("GroupJob");
        quartzBean.setJobClass(DownloadJob.class);
        JobDataMap jobDataMap = new JobDataMap();
        jobDataMap.put("one", "Test");
        quartzBean.setJobDataMap(jobDataMap);
        quartzService.createJob(quartzBean);
        return "SUCCESS";
    }
}

原创文章,作者:jiafegn,如若转载,请注明出处:https://www.techlearn.cn/archives/1977

Previous 2023年4月30日
Next 2023年12月31日

相关推荐

  • Springboot 注解 @Resource

    作用 和@Autowired注解类似,也是用来注入依赖对象的,spring容器会对bean中所有的字段、方法进行遍历,标注有@Resouce注解的,都会进行注入。@Autowire…

    springboot 2022年9月14日
    270
  • SpringBoot 全局异常统一处理

    前言 在实际的项目开发过程中,会有大量的异常发生,而我们并不能将异常信息反馈到用户,所以在返回结果的时候需要对异常进行处理,可是如果在每个Controller返回结果都需要进行异常…

    springboot 2023年4月29日
    125
  • SpringBoot 注解 @Autowired

    作用 实现依赖注入查找方式:@Autowired是先到容器查找类型,如果该类型只有一个那么就直接注入,有多个时再根据名字找使用范围:构造器、方法、参数、字段、注解参数:requir…

    springboot 2022年9月9日
    405
  • SpringBoot 缓存 – Redis

    引入依赖 缓存配置 启用缓存 修改项目启动类,增加注解@EnableCaching,开启缓存功能,如下: 配置缓存类 新建Redis缓存配置类RedisConfig,如下: 添加缓…

    2023年4月23日
    161
  • SpringBoot 打包

    SpringBoot 项目部署到服务器常见的方式就是,打包成 jar 包,通过 nohup java -jar 命令去运行项目,这也是官方推荐的一种方式。 导入依赖 打包 设置打包…

    springboot 2023年4月5日
    148
  • SpringBoot 整合SpringSecurity认证

    简介 Spring Security 是Spring家族中的一个安全管理框架。相比与另外一个安全框架shiro,它提供了更丰富的功能,社区资源也比Shiro丰富。 —般Web应用的…

    2023年12月31日
    424
  • SpringBoot 整合Mybatis

    简介 MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBat…

    2024年8月28日
    309
  • Springboot 注解大全-@Import

    作用 @Import可以用来批量导入需要注册的各种类,如普通的类、配置类,完成普通类和配置类中所有bean注册到spring容器中。作用范围:作用于类、注解 定义 参数 value…

    springboot 2022年8月15日
    184
  • SpringBoot 整合Mybatis-Plus

    简介 MyBatis-Plus是一个MyBatis的增强工具,在Mybatis的基础上只做增强不做改变,为简化开发,提高效率而生。 添加依赖 注意:添加Mybatis-Plus即可…

    springboot 2023年3月26日
    131
  • Springboot 注解 @ConfigurationProperties

    作用 @ConfigurationProperties注解主要作用就是将prefix属性指定的前缀配置项的值绑定到这个Bean上,默认情况下需要和@Component或者@Conf…

    springboot 2022年9月2日
    181