分布式定时任务 quartz

架构

  • Trigger
    Trigger用于定义调度任务的时间规则,在Quartz中主要有四种类型的Trigger:SimpleTrigger、CronTrigger、DataIntervalTrigger和NthIncludedTrigger。

SimpleTrigger:简单的触发器
CalendarIntervalTrigger:日历触发器
CronTrigger:Cron表达式触发器
DailyTimeIntervalTrigger:日期触发器
NthIncludedDayTrigger:它设计用于在每一间隔类型的第几天执行 Job。

  • Job&Jodetail
    Quartz将任务分为Job、JobDetail两部分,其中Job用来定义任务的执行逻辑,而JobDetail用来描述Job的定义(例如Job接口的实现类以及其他相关的静态信息)。对Quartz而言,主要有两种类型的Job,StateLessJob、StateFulJob

  • Scheduler
    实际执行调度逻辑的控制器,Quartz提供了DirectSchedulerFactory和StdSchedulerFactory等工厂类,用于支持Scheduler相关对象的产生。

接入Springboot

依赖

添加到springboot依赖中

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

数据库表

qrtz_blob_triggers : 以Blob 类型存储的触发器。
qrtz_calendars:存放日历信息,quartz可配置一个日历来指定一个时间范围。
qrtz_cron_triggers:存放cron类型的触发器。
qrtz_fired_triggers:存放已触发的触发器。
qrtz_job_details:存放一个jobDetail信息。
qrtz_job_listeners:job监听器。
qrtz_locks: 存储程序的悲观锁的信息(假如使用了悲观锁)。
qrtz_paused_trigger_graps:存放暂停掉的触发器。
qrtz_scheduler_state:调度器状态。
qrtz_simple_triggers:简单触发器的信息。
qrtz_trigger_listeners:触发器监听器。
qrtz_triggers:触发器的基本信息。

https://github.com/quartz-scheduler/quartz/blob/d42fb7770f287afbf91f6629d90e7698761ad7d8/quartz-core/src/main/resources/org/quartz/impl/jdbcjobstore/tables_oracle.sql

配置文件

org.quartz.scheduler.instanceName = quartzScheduler
org.quartz.scheduler.instanceId = AUTO


org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
# 开启集群
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.useProperties = false
org.quartz.jobStore.clusterCheckinInterval = 20000


org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

配置类


@Configuration
@Slf4j
public class QuartzSchedulerConfig {

    // 配置
    private static final String QUARTZ_PROPERTIES_NAME = "/quartz.properties";

    // job 容器化
    class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
        private transient AutowireCapableBeanFactory beanFactory;

        @Override
        public void setApplicationContext(final ApplicationContext context) {
            beanFactory = context.getAutowireCapableBeanFactory();
        }

        @Override
        protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
            final Object job = super.createJobInstance(bundle);
            beanFactory.autowireBean(job);
            return job;
        }
    }

    @Bean
    public JobFactory jobFactory(ApplicationContext applicationContext) {
        AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
        jobFactory.setApplicationContext(applicationContext);
        return jobFactory;
    }

    @Bean
    public Properties quartzProperties() throws IOException {
        // 读取配置
        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
        propertiesFactoryBean.setLocation(new ClassPathResource(QUARTZ_PROPERTIES_NAME));
        propertiesFactoryBean.afterPropertiesSet();
        return propertiesFactoryBean.getObject();
    }

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory, DataSource dataSource) {
        SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
        try {
            factoryBean.setQuartzProperties(quartzProperties());
            factoryBean.setDataSource(dataSource); // 配置数据源druid
            factoryBean.setJobFactory(jobFactory);
            factoryBean.setOverwriteExistingJobs(true);
        } catch (Exception e) {
            log.error("Failed to load config file {}.", QUARTZ_PROPERTIES_NAME, e);
            throw new RuntimeException("LoadingConfigFileError", e);
        }

        return factoryBean;
    }
}

Job实体类

@Data
@TableName("qrtz_job_tb")
public class QuartzJobEntity {
    //job名称
    private String jobName;

    //job组
    private String jobGroup;

    //cron 表达式
    private String cron;

    private String jobClass;

    private String jobData;

    private String status;

}

Dao

@Mapper
public interface QuartzJobEntityDao extends BaseMapper<QuartzJobEntity> {
}

service


@Service
@Slf4j
public class QuartzServiceImpl implements QuartzService {
    @Autowired
    QuartzJobEntityDao jobEntityDao;

    @Autowired
    Scheduler scheduler;

    @Override
    public List<QuartzJobEntity> getAllJobs() {
        return jobEntityDao.selectList(new LambdaQueryWrapper<QuartzJobEntity>()
        .eq(QuartzJobEntity::getStatus, "OPEN"));
    }

    @Override
    public QuartzJobEntity getJobByName(String jobName) {
        return jobEntityDao.selectOne(new LambdaQueryWrapper<QuartzJobEntity>()
                .eq(QuartzJobEntity::getJobName, jobName));
    }

    @Override
    public JobDataMap getJobDataMap(QuartzJobEntity job) {
        JobDataMap map = new JobDataMap();
        map.put("name", job.getJobName());
        map.put("group", job.getJobGroup());
        map.put("cronExpression", job.getCron());
        map.put("data", job.getJobData());
        map.put("status", job.getStatus());
        return map;
    }

    @Override
    public JobKey getJobKey(QuartzJobEntity job) {
        return JobKey.jobKey(job.getJobName(), job.getJobGroup());
    }

    @Override
    public JobDetail geJobDetail(JobKey jobKey, JobDataMap map, Class t) {
        return JobBuilder.newJob(t)
                .withIdentity(jobKey)
                .setJobData(map)
                .storeDurably()
                .build();
    }

    @Override
    public Trigger getTrigger(QuartzJobEntity job) {
        return TriggerBuilder.newTrigger()
                .withIdentity(job.getJobName(), job.getJobGroup())
                .withSchedule(CronScheduleBuilder.cronSchedule(job.getCron()))
                .build();
    }

    @Override
    public void stopJob(String jobName) {
        try {
            QuartzJobEntity job = getJobByName(jobName);
            JobKey jobKey = getJobKey(job);
            scheduler.pauseJob(jobKey);
            scheduler.deleteJob(jobKey);
        } catch (SchedulerException e) {
            log.warn("Job stop fail!!!");
        }
    }

    @Override
    public void startJob(String jobName) {
        try {
            QuartzJobEntity job = getJobByName(jobName);
            if ("OPEN".equals(job.getStatus())) {
                JobKey jobKey = getJobKey(job);
                JobDataMap jobDataMap = getJobDataMap(job);
                JobDetail jobDetail = geJobDetail(jobKey, jobDataMap, Class.forName(job.getJobClass()));
                scheduler.scheduleJob(jobDetail, getTrigger(job));
            } else {
                log.warn("Job start fail, status is not OPEN!!!");
            }
        } catch (Exception e) {
            log.warn("job start fail!!!");
        }
    }

    @Override
    public void restartJob(String jobName) {
        stopJob(jobName);
        startJob(jobName);
    }
}

接口


@RestController
@RequestMapping(value = "/quartz", method = RequestMethod.POST)
@Slf4j
public class QuartzController {
    @Autowired
    QuartzService quartzService;

    @Autowired
    Scheduler scheduler;

    @PostConstruct
    public void init() throws SchedulerException, ClassNotFoundException {
        restartAll();
    }

    @PostMapping("/get_all")
    @ResponseBody
    public List<QuartzJobEntity> getAll() {
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        Set<JobKey> jobKeys;
        List<QuartzJobEntity> jobList = new ArrayList<>();
        try {
            jobKeys = scheduler.getJobKeys(matcher);
            for (JobKey jobKey : jobKeys) {
                List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
                for (Trigger trigger : triggers) {
                    QuartzJobEntity job = new QuartzJobEntity();
                    job.setJobName(jobKey.getName());
                    job.setJobGroup(jobKey.getGroup());
                    Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                    job.setStatus(triggerState.name());
                    if (trigger instanceof CronTrigger) {
                        CronTrigger cronTrigger = (CronTrigger) trigger;
                        String cronExpression = cronTrigger.getCronExpression();
                        job.setCron(cronExpression);
                    }
                    jobList.add(job);
                }
            }

        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return jobList;
    }

    @PostMapping("/stop/{jobName}")
    public ReturnT stop(@PathVariable String jobName) {
        quartzService.stopJob(jobName);
        return ReturnT.SUCCESS;
    }

    @PostMapping("/stop_all")
    @ResponseBody
    public ReturnT stopAll() throws SchedulerException {
        Set<JobKey> set = scheduler.getJobKeys(GroupMatcher.anyGroup());
        scheduler.pauseJobs(GroupMatcher.anyGroup()); //暂停所有JOB
        for (JobKey jobKey : set) { //删除从数据库中注册的所有JOB
            scheduler.unscheduleJob(TriggerKey.triggerKey(jobKey.getName(), jobKey.getGroup()));
            scheduler.deleteJob(jobKey);
        }
        return ReturnT.SUCCESS;
    }

    @PostMapping("/start/{jobName}")
    public ReturnT start(@PathVariable String jobName) {
        quartzService.startJob(jobName);
        return ReturnT.SUCCESS;
    }

    @PostMapping("/start_all")
    @ResponseBody
    public ReturnT startAll() throws SchedulerException, ClassNotFoundException {
        for (QuartzJobEntity job : quartzService.getAllJobs()) {                               //从数据库中注册的所有JOB
            log.info("Job register name : {} , group : {} , cron : {}", job.getJobName(), job.getJobGroup(), job.getCron());
            JobDataMap map = quartzService.getJobDataMap(job);
            JobKey jobKey = quartzService.getJobKey(job);
            JobDetail jobDetail = quartzService.geJobDetail(jobKey, map, Class.forName(job.getJobClass()));
            scheduler.scheduleJob(jobDetail, quartzService.getTrigger(job));
        }
        return ReturnT.SUCCESS;
    }

    @PostMapping("/restart/{jobName}")
    public ReturnT restart(@PathVariable String jobName) {
        quartzService.restartJob(jobName);
        return ReturnT.SUCCESS;
    }

    @PostMapping("/restart_all")
    public ReturnT restartAll() throws SchedulerException, ClassNotFoundException {
        synchronized (this) {
            stopAll();
            startAll();
        }

        return ReturnT.SUCCESS;
    }
}

JOB实例

public class DateTimeJob extends QuartzJobBean {

    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        //获取JobDetail中关联的数据
        String msg = (String) jobExecutionContext.getJobDetail().getJobDataMap().get("msg");
        System.out.println("current time :"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "---" + msg);
    }
}

Springboot自动配置改造

  1. 去除 QuartzSchedulerConfig 配置类

  2. maven依赖

<!--spring boot集成quartz-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
  1. 添加application.yaml风格配置文件
spring:
  quartz:
    #相关属性配置
    properties:
      org:
        quartz:
          scheduler:
            # 多个调度器使用同一个值,保证job执行的唯一性
            instanceName: defaultScheduler
            # 使用自定义id时必须为AUTO
            instanceId: AUTO
            instanceIdGenerator:
              # 使用自定义id生成器
              class: com.nobitan.util.UserNameIdGen
          jobStore:
            class: org.quartz.impl.jdbcjobstore.JobStoreTX
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
            tablePrefix: QRTZ_
            isClustered: true
            clusterCheckinInterval: 10000
            useProperties: false
          threadPool:
            class: org.quartz.simpl.SimpleThreadPool
            threadCount: 10
            threadPriority: 5
            threadsInheritContextClassLoaderOfInitializingThread: true
posted @ 2022-07-04 00:07  熊云港  阅读(439)  评论(0编辑  收藏  举报