首先quartz本身是支持分布式的,通过表来管理各节点之间的关系。
1.去quartz官网下载最新的包 http://www.quartz-scheduler.org/
2、下载之后解压,进入如下目录,创建数据库表
quartz-2.2.3-distribution\quartz-2.2.3\docs\dbTables(选择对应的数据库SQL)
11张表功能说明:
3、创建springboot项目
4.创建 quartz.properties配置文件
org.quartz.scheduler.instanceId=AUTO org.quartz.scheduler.makeSchedulerThreadDaemon=true org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.makeThreadsDaemons=true #线程数量 org.quartz.threadPool.threadCount:20 #线程优先级 org.quartz.threadPool.threadPriority:5 org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate org.quartz.jobStore.tablePrefix=QRTZ_ #特别注意:此处是quartz的数据源,报错就debug跟踪一下查看dbName org.quartz.jobStore.dataSource = springTxDataSource.schedulerFactoryBean #加入集群 org.quartz.jobStore.isClustered=true #容许的最大作业延 org.quartz.jobStore.misfireThreshold=25000 #调度实例失效的检查时间间隔 org.quartz.jobStore.clusterCheckinInterval: 5000
5. quartz的初始化配置
@Configuration public class SchedulerConfig { // 配置文件路径 private static final String QUARTZ_CONFIG = "/quartz.properties"; // 按照自己注入的数据源自行修改 @Qualifier("writeDataSource") @Autowired private DataSource dataSource; @Autowired private AutoWiredSpringBeanToJobFactory autoWiredSpringBeanToJobFactory; /** * 从quartz.properties文件中读取Quartz配置属性 * @return * @throws IOException */ @Bean public Properties quartzProperties() throws IOException { PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); propertiesFactoryBean.setLocation(new ClassPathResource(QUARTZ_CONFIG)); propertiesFactoryBean.afterPropertiesSet(); return propertiesFactoryBean.getObject(); } /** * JobFactory与schedulerFactoryBean中的JobFactory相互依赖,注意bean的名称 * 在这里为JobFactory注入了Spring上下文 * * @param applicationContext * @return */ @Bean public JobFactory buttonJobFactory(ApplicationContext applicationContext) { AutoWiredSpringBeanToJobFactory jobFactory = new AutoWiredSpringBeanToJobFactory(); jobFactory.setApplicationContext(applicationContext); return jobFactory; } @Bean public SchedulerFactoryBean schedulerFactoryBean() throws IOException { SchedulerFactoryBean factory = new SchedulerFactoryBean(); factory.setJobFactory(autoWiredSpringBeanToJobFactory); factory.setOverwriteExistingJobs(true); factory.setAutoStartup(true); // 设置自行启动 // 延时启动,应用启动1秒后 factory.setStartupDelay(1); factory.setQuartzProperties(quartzProperties()); factory.setDataSource(dataSource);// 使用应用的dataSource替换quartz的dataSource return factory; } }
6.任务工厂注入到Spring
/** * 为JobFactory注入SpringBean,否则Job无法使用Spring创建的bean */ @Component public class AutoWiredSpringBeanToJobFactory 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; } }
7.创建任务调度管理
public class QuartzManager { private static SchedulerFactory schedulerFactory = new StdSchedulerFactory(); private Scheduler scheduler = null; /** * @Description: 添加一个定时任务 * * @param jobName 任务名 * @param jobGroupName 任务组名 * @param triggerName 触发器名 * @param triggerGroupName 触发器组名 * @param jobClass 任务 * @param cron 时间设置,参考quartz说明文档 */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static void addJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName, Class jobClass, String cron) { try { // 任务名,任务组,任务执行类 Scheduler scheduler = schedulerFactory.getScheduler(); JobDetail jobDetail= JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build(); // 触发器 TriggerBuildertriggerBuilder = TriggerBuilder.newTrigger(); // 触发器名,触发器组 triggerBuilder.withIdentity(triggerName, triggerGroupName); triggerBuilder.startNow(); // 触发器时间设定 triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron)); // 创建Trigger对象 CronTrigger trigger = (CronTrigger) triggerBuilder.build(); // 调度容器设置JobDetail和Trigger scheduler.scheduleJob(jobDetail, trigger); // 启动 if (!scheduler.isShutdown()) { scheduler.start(); } } catch (Exception e) { throw new RuntimeException(e); } } /** * @Description: 修改一个任务的触发时间 * * @param jobName * @param jobGroupName * @param triggerName 触发器名 * @param triggerGroupName 触发器组名 * @param cron 时间设置,参考quartz说明文档 */ public static void modifyJobTime(String jobName,String jobGroupName, String triggerName, String triggerGroupName, String cron) { try { Scheduler scheduler = schedulerFactory.getScheduler(); TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName); CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); if (trigger == null) { return; } String oldTime = trigger.getCronExpression(); if (!oldTime.equalsIgnoreCase(cron)) { System.out.println("任务:"+jobName+"被修改"); /** 方式一 :调用 rescheduleJob 开始 */ /* // 触发器 TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger(); // 触发器名,触发器组 triggerBuilder.withIdentity(triggerName, triggerGroupName); triggerBuilder.startNow(); // 触发器时间设定 triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron)); // 创建Trigger对象 trigger = (CronTrigger) triggerBuilder.build(); // 方式一 :修改一个任务的触发时间 scheduler.rescheduleJob(triggerKey, trigger);*/ /** 方式一 :调用 rescheduleJob 结束 */ /** 方式二:先删除,然后在创建一个新的Job */ JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(jobName, jobGroupName)); Class extends Job> jobClass = jobDetail.getJobClass(); removeJob(jobName, jobGroupName, triggerName, triggerGroupName); addJob(jobName, jobGroupName, triggerName, triggerGroupName, jobClass,cron); /** 方式二 :先删除,然后在创建一个新的Job */ } } catch (Exception e) { throw new RuntimeException(e); } } /** * @Description: 移除一个任务 * * @param jobName * @param jobGroupName * @param triggerName * @param triggerGroupName */ public static void removeJob(String jobName, String jobGroupName,String triggerName, String triggerGroupName) { try { Scheduler scheduler = schedulerFactory.getScheduler(); TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName); scheduler.pauseTrigger(triggerKey);// 停止触发器 scheduler.unscheduleJob(triggerKey);// 移除触发器 scheduler.deleteJob(JobKey.jobKey(jobName, jobGroupName));// 删除任务 } catch (Exception e) { throw new RuntimeException(e); } } /** * @Description:启动所有定时任务 */ public static void startJobs() { try { Scheduler scheduler = schedulerFactory.getScheduler(); scheduler.start(); } catch (Exception e) { throw new RuntimeException(e); } } /** * @Description:关闭所有定时任务 */ public static void shutdownJobs() { try { Scheduler scheduler = schedulerFactory.getScheduler(); if (!scheduler.isShutdown()) { scheduler.shutdown(); } } catch (Exception e) { throw new RuntimeException(e); } } /** * 获取当前正在执行的任务 * @return */ public static boolean getCurrentJobs(String name){ try { Scheduler scheduler = schedulerFactory.getScheduler(); List jobContexts = scheduler.getCurrentlyExecutingJobs(); for (JobExecutionContext context : jobContexts) { if (name.equals(context.getTrigger().getJobKey().getName())) { return true; } } } catch (Exception e) { throw new RuntimeException(e); } return false; } public Scheduler getScheduler() { return scheduler; } public void setScheduler(Scheduler scheduler) { this.scheduler = scheduler; } }
8.创建一个执行的Job
/** * @DisallowConcurrentExecution : 此标记用在实现Job的类上面,意思是不允许并发执行. * 注org.quartz.threadPool.threadCount的数量有多个的情况,@DisallowConcurrentExecution才生效 */ @DisallowConcurrentExecution public class ButtonTimerJob implements Job { private static final Logger logger = LoggerFactory.getLogger(ButtonTimerJob.class); /** * 核心方法,Quartz Job真正的执行逻辑. * @param JobExecutionContext中封装有Quartz运行所需要的所有信息 * @throws JobExecutionException execute()方法只允许抛出JobExecutionException异常 */ @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { logger.info("--------------定时任务执行逻辑---------------------"); } }
9.创建启动Job类
@Configuration public class StartJob implements ApplicationListener{ private Logger logger = LoggerFactory.getLogger(this.getClass()); public void run(){ logger.info(">> 启动定时任务..."); // QuartzManager.startJobs(); QuartzManager.addJob( "SpecialPeriodJob", "SpecialPeriodJobGroup", "SpecialPeriodTrigger", "SpecialPeriodTriggerGroup", ButtonTimerJob.class, "0/30 * * * * ?"); } @Override public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { System.out.println("启动定时任务......"); run(); } }
注:第一次启动后定时任务会自动写入到数据库,再次启动后报任务存在的错误,就不需要在添加Job了,直接执行启动任务即可。当启动多台服务时,第一台启动的会执行定时任务,当第一台服务挂了,第二台服务继续执行。