quartz开源任务调度框架
0.集群和分布式
我的理解:
集群:同一个服务部署多个服务器,也就是单机版多部署几台服务器,一台不能用了,另一台还可以接着用(调度)
分布式:多个集群组成一个完整项目,系统之间的耦合度降低,服务的复用性更高,分成了多个子系统
**:
1.Quartz是什么?(任务调度框架)
Quartz官方网站:http://www.quartz-scheduler.org
1. Quartz是一个功能丰富的、开源任务调度框架,几乎可以集成到任何Java应用中,小 到单体引用,大到大型电子商务系统;
2. Quartz可以用来创建和执行成千上万的简单或复杂调度任务;
3. Quartz的任务被定义成一组标准的Java组件,几乎可以执行任何编程任务;
4. Quartz包含很多企业级功能,包括JTA事务(分布式事务)和集群
2.Quartz可以做什么
1. 定时发送邮件、短信
2. 定时进行数据同步
3. 定时删除超时订单
3.Quartz的核心API
1. Job:你想要调度器执行的任务组件需要实现的接口
2. JobDetail:描述任务详细细节(描述任务名称、任务组)
3. JobBuilder:用户创建JobDetail
4. Trigger:定义任务的调度时机(规则)
SimpleTrigger:定义任务开始时间、结束时间、每隔多长时间执行、执行多少次、 执行多久
CronTrigger:使用Cron表达式定义执行规则
5. TriggerBudiler:创建Trigger触发器
6. Scheduler:使用Trigger定义规则去调度(执行)任务
7. JobDataMap:JobDetail和Trigger可以通过它给Job传递参数
8. JobKey: 封装了Job的name和group("工作的名称", "工作的组") TriggerKey: ("触发器的名称", "触发器的组")
9. JobExectionContext
4.代码
4.1创建Quartz的过程
First
package com.etoak.simple; import com.etoak.job.HelloJob; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; public class First { public static void main(String[] args) throws SchedulerException { //1.创建jobDetail //JObKey封装了Job的name和group JobKey jobKey = new JobKey("hello","hello"); //定义工作并将其绑定到我们的HelloJob类 //.withIdentity("工作的名称", "工作的组") JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)//创建了一个JobDetail,绑定任务job .withIdentity(jobKey) .build(); //2.创建Trigger(触发器)==>定义规则 TriggerKey triggerKey = new TriggerKey("hello","hello"); //TriggerBuilder - 用于定义/构建触发器实例。 //.withIdentity("触发器的名称", "触发器的组") SimpleTrigger trigger = TriggerBuilder.newTrigger() .withIdentity(triggerKey) .withSchedule(SimpleScheduleBuilder //SimpleScheduleBuilder是简单调用触发器,它只能指定触发的间隔时间和执行次数; .simpleSchedule()//创建一个SimpleScheduleBuilder .withIntervalInSeconds(5)//每个五秒执行一次 .repeatForever()//一直执行 ).build(); //3.创建Scheduler==>执行Trigger定义的规则 Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); scheduler.scheduleJob(jobDetail,trigger); scheduler.start(); } }
==========================================================================================================================================
Second
package com.etoak.simple;
import com.etoak.job.HelloJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Second {
public static void main(String[] args) throws SchedulerException {
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
.withIdentity("hello").build();
Date current = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("当前时间->" + sdf.format(current));
Date date = new Date(current.getTime() + 5000L);
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("hello")
.startAt(date)//表示触发器首次被触发的时间;
.withSchedule(SimpleScheduleBuilder
.simpleSchedule()
.withIntervalInSeconds(5)//每个五秒执行一次
.withRepeatCount(10)//执行十次,加上第一次是十一次
).build();
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.scheduleJob(jobDetail,trigger);
scheduler.start();
}
}
==============================================================================================================================
Third
package com.etoak.simple;
import com.etoak.job.HelloJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.util.Date;
public class Third {
public static void main(String[] args) throws SchedulerException {
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
.withIdentity("hello")
.build();
//获取30秒后的时间
Date date = new Date(System.currentTimeMillis() + 1000L * 30);
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("hello")
.endAt(date)//30秒后结束
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(5)
.repeatForever()
).build();
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.scheduleJob(jobDetail,trigger);
scheduler.start();
}
}
===========================================依赖===================================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.etoak.et2006.quartz</groupId>
<artifactId>quartz-java</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--quartz依赖-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
<!--日志-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
</project>
5.测试Cron表达式
package com.etoak.cron; import com.etoak.job.HelloJob; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; public class CronTest { public static void main(String[] args) throws SchedulerException { JobDetail jobDetail = JobBuilder.newJob(HelloJob.class) .withIdentity("hello")//身份信息 .build(); CronTrigger trigger = TriggerBuilder.newTrigger() .withIdentity("hello") .withSchedule( CronScheduleBuilder//.cronSchedule("*/5 * * * * ?")//秒分时天月周 //.cronSchedule("0 33 11 ? * MON-FRI") .cronSchedule("0 0/1 11 ? * MON-FRI") ).build(); Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); scheduler.scheduleJob(jobDetail,trigger); scheduler.start(); } }
6.SpringMVC整合单机版Quartz
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--单机版--> <!--1.创建任务类--> <bean id="emailJob" class="com.etoak.job.EmailJob" /> <!--2.JobDetail--> <bean id="emailJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <!-- 任务名称和任务组名称 --> <property name="name" value="emailJob" /> <property name="group" value="emailJob" /> <!-- 执行的目标任务对象(spring bean) --> <property name="targetObject" ref="emailJob" /> <!--任务对象的方法--> <property name="targetMethod" value="sendEmail" /> </bean> <!--3.Trigger--> <bean id="emailTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> <property name="name" value="emailTrigger" /> <property name="group" value="emailTrigger" /> <property name="jobDetail" ref="emailJobDetail"/> <property name="cronExpression" value="*/5 * * ? * *" /> </bean> <!--4.调度--> <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="emailTrigger" /> </list> </property> </bean> </beans>
7.SpringMVC整合集群版Quartz(要求任务对象必须extends QuartzJobBean)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--集群版--> <!--数据源--> <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/et2006" /> <property name="username" value="root" /> <property name="password" value="etoak" /> </bean> <!-- 事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- JobDetailFactoryBean --> <bean id="orderJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean"> <property name="name" value="orderJob" /> <property name="group" value="orderJob" /> <!--jobClass--> <property name="jobClass" value="com.etoak.job.OrderJob" /> <!--durability:持久化任务--> <property name="durability" value="true" /> </bean> <!-- CronTriggerFactoryBean --> <bean id="orderTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> <property name="name" value="orderTrigger" /> <property name="group" value="orderTrigger"/> <property name="jobDetail" ref="orderJobDetail" /><!--ref写成了value--> <property name="cronExpression" value="0/5 * * * * ?" /> </bean> <!-- SchedulerFactoryBean --> <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="transactionManager" ref="transactionManager" /> <!-- 集群配置 --> <property name="configLocation" value="classpath:quartz.properties" /> <property name="triggers"> <list> <ref bean="orderTrigger" /> </list> </property> <!--第一种--> <property name="applicationContextSchedulerContextKey" value="delete" /> <!--第二种--> <property name="jobFactory" ref="mvcJobFactory" /> </bean> </beans>
================================两种整合用到的依赖===============================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.etoak.et2006.quartz</groupId>
<artifactId>quartz-springmvc</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>quartz-springmvc Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!-- spring-context-support -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!-- spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!-- jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.3</version>
</dependency>
<!-- logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
</dependencies>
<build>
<finalName>quartz-springmvc</finalName>
</build>
</project>
==============================web.xml===================================================
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<!-- spring Root容器(父容器) -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-root.xml</param-value>
</context-param>
<!-- POST请求编码过滤器 -->
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener><!--监听器--><!--webapp爆红因为违反了dtd规范(标签顺序问题)-->
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 前端控制器 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
8.springBoot整合Quartz
8.1
/** * 配置单机版的Quartz任务 * */ //@Configuration:测试Job/resume重新启动方法时需要把这个注释掉 // 标识这是一个spring容器 public class StandaloneConfig { @Autowired EmailJob emailJob; @Bean(name = "emailJobDetail") public MethodInvokingJobDetailFactoryBean emailJobDetail(){ MethodInvokingJobDetailFactoryBean factoryBean = new MethodInvokingJobDetailFactoryBean(); factoryBean.setName("emailJob"); factoryBean.setGroup("emailJob"); factoryBean.setTargetObject(this.emailJob); factoryBean.setTargetMethod("send"); return factoryBean; } @Bean public CronTriggerFactoryBean emailTrigger(){ CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean(); factoryBean.setName("emailTrigger"); factoryBean.setGroup("emailTrigger"); factoryBean.setJobDetail(this.emailJobDetail().getObject()); factoryBean.setCronExpression("0/5 * * * * ?"); return factoryBean; } @Bean public SchedulerFactoryBean scheduler(){ SchedulerFactoryBean factoryBean = new SchedulerFactoryBean(); factoryBean.setTriggers(this.emailTrigger().getObject()); return factoryBean; } }
8.2 /** * 集群配置 * * 数据源 * 事务管理器 *JobDetailFactoryBean *CronTriggerFactoryBean *SchedulerFactoryBean */ @Configuration public class ClusterConfig { /*注入数据源*/ @Autowired DataSource dataSource; /*事务管理器*/ @Bean public PlatformTransactionManager transactionManager(){ return new DataSourceTransactionManager(this.dataSource); } @Bean public JobDetailFactoryBean orderJobDetail(){ JobDetailFactoryBean factoryBean = new JobDetailFactoryBean(); factoryBean.setName("orderJob"); factoryBean.setGroup("orderJob"); factoryBean.setJobClass(OrderJob.class); factoryBean.setDurability(true);//持久化 return factoryBean; } @Bean public CronTriggerFactoryBean orderTrigger(){ CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean(); factoryBean.setName("orderTrigger"); factoryBean.setGroup("orderTrigger"); factoryBean.setJobDetail(this.orderJobDetail().getObject()); factoryBean.setCronExpression("0/5 * * * * ?"); return factoryBean; } @Autowired MvcJobFactoryBean mvcJobFactoryBean;//第二种方法,其实就是把job注入到了spring容器里; //而第一种则是得到spring容器,通过spring容器得到OrderService.class这个Bean @Bean public SchedulerFactoryBean Scheduler(//方法名不能一样,方法名相当于id,@Bean后边参数可以取别名 /*类型重复可以用这个注解指定用哪个*/ @Qualifier("transactionManager") PlatformTransactionManager transactionManager ){ SchedulerFactoryBean factoryBean = new SchedulerFactoryBean(); factoryBean.setDataSource(this.dataSource); //factoryBean.setTransactionManager(transactionManager); ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); Resource resource = resolver.getResource("classpath:quartz.properties"); factoryBean.setConfigLocation(resource); factoryBean.setTriggers(this.orderTrigger().getObject()); factoryBean.setApplicationContextSchedulerContextKey("spring");//第一种 factoryBean.setJobFactory(this.mvcJobFactoryBean);//第二种 return factoryBean; } }
8.3解决集群中的Quartz任务无法使用Spring Bean的问题
第一种:使用applicationContextSchedulerContextKey属性
@Override protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { Scheduler scheduler = jobExecutionContext.getScheduler();//第一种 try { SchedulerContext context = scheduler.getContext(); ApplicationContext ioc = (ApplicationContext) context.get("spring"); OrderService orderService = ioc.getBean(OrderService.class);*//*context.get("spring")参数写错,所以ioc为空,orderService也为空*//* orderService.delOrder(); } catch (SchedulerException e) { e.printStackTrace(); } System.out.println("删除超时订单"); }
第二种:继承SpringBeanJobFactory
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
orderService.delOrder();
}
@Component
public class MvcJobFactoryBean extends SpringBeanJobFactory {
@Autowired
AutowireCapableBeanFactory ioc;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {//第二种
Object job = super.createJobInstance(bundle);//创建job任务
ioc.autowireBean(job);//把任务重新注入spring容器中
return job;
}
}
===============================================================================================
@Bean
public SchedulerFactoryBean Scheduler(//方法名不能一样,方法名相当于id,@Bean后边参数可以取别名
/*类型重复可以用这个注解指定用哪个*/
@Qualifier("transactionManager") PlatformTransactionManager transactionManager
){
SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
factoryBean.setDataSource(this.dataSource);
//factoryBean.setTransactionManager(transactionManager);
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource resource = resolver.getResource("classpath:quartz.properties");
factoryBean.setConfigLocation(resource);
factoryBean.setTriggers(this.orderTrigger().getObject());
factoryBean.setApplicationContextSchedulerContextKey("spring");//第一种
factoryBean.setJobFactory(this.mvcJobFactoryBean);//第二种
return factoryBean;
}
8.4测试暂停/重新运行/创建任务的方法
@RestController @RequestMapping("/job") public class JobController { @Autowired Scheduler scheduler; @RequestMapping("/pause")//暂停 public String pause(@RequestParam String name, @RequestParam String group) throws SchedulerException { TriggerKey triggerKey = new TriggerKey(name,group); if(!scheduler.checkExists(triggerKey)){ return "任务不存在"; } scheduler.pauseTrigger(triggerKey); return "success"; } @RequestMapping("/resume")//重新运行 public String resume(@RequestParam String name, @RequestParam String group) throws SchedulerException { TriggerKey triggerKey = new TriggerKey(name,group); if(!scheduler.checkExists(triggerKey)){ return "任务不存在"; } Trigger.TriggerState triggerState = scheduler.getTriggerState(triggerKey); if(!Trigger.TriggerState.PAUSED.equals(triggerState)){ return "任务不是暂停状态"; } scheduler.resumeTrigger(triggerKey); return "success"; } @RequestMapping("/create") public String create(@RequestParam String jobName, @RequestParam String jobGroup, @RequestParam String triggerName, @RequestParam String triggerGroup, @RequestParam String cron) throws ParseException, SchedulerException { JobDetailImpl jobDetail = new JobDetailImpl(); jobDetail.setName(jobName); jobDetail.setGroup(jobGroup); jobDetail.setJobClass(SmsJob.class); CronTriggerImpl cronTrigger = new CronTriggerImpl(); cronTrigger.setName(triggerName); cronTrigger.setGroup(triggerGroup); cronTrigger.setCronExpression(cron); scheduler.scheduleJob(jobDetail,cronTrigger); return "success"; } }