1、quartz
1.1 概述
Quartz是OpenSymphony开源组织在Job scheduling领域的一个开源项目,由Java开发,可以用来执行定时任务,类似于java.util.Timer。Quartz是功能强大的开源作业调度库,几乎可以集成到任何Java应用程序中。Quartz包含许多企业级功能,例如对JTA事务和集群的支持。Quartz作为定时任务组件,既可以单独使用,也可以整合Spring使用。
1.2 核心
1.2.1 api
Scheduler:任务调度器,使任务和触发器关联,统一进行任务的调度
StdSchedulerFactory:任务调度器工厂,可以创建Scheduler对象
JobDetail:任务对象
JobBuilder:任务构建器,用于创建JobDetail对象
Trigger:触发器对象
TriggerBuilder:触发器构建器,用于创建Trigger对象
JobExecutionContext:任务执行的上下文对象,通过此对象可以获取当前执行任务的相关信息,例如JobDetail、Trigger对象都可以获取到
JobDataMap:保存任务实例的状态信息
RAMJobStore:此类实现了一个利用RAM作为其存储设备的JobStore,访问速度极快,但是数据却易失,如果需要在程序关闭之前保持真正的持久性,则不应使用此JobStore
JobStoreTX:通过JDBC将所有数据保存在数据库中
1.2.2 存储
- JobStoreTX
通过上下文对象JobExecutionContext可以看到任务的存储是通过RAMJobStore实现的,这种方式的任务信息是保存在内存中的,如果服务重启后则任务信息都会丢失。
- 数据模型
Quartz官方提供了一套数据模型(共11张表)用于保存定时任务相关信息,建表脚本:
1.3 示例
- 依赖
<!--quartz 定时任务-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.3.0</version>
</dependency>
- 消除debug日志
resource中配置logback.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
</configuration>
- job
// 自定义job
public class HelloJob implements Job {
public HelloJob() {
System.out.println("hellojob被创建了");
}
/**
* 当任务触发时执行此方法
* @param context
* @throws JobExecutionException
*/
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
Set<Map.Entry<String, Object>> entries = jobDataMap.entrySet();
for (Map.Entry<String, Object> entry : entries) {
System.out.println(entry.getKey()+":"+entry.getValue());
}
System.out.println("自定义job执行时间"+ LocalDateTime.now());
}
}
- scheduler,trigger
public class HelloJobMain {
public static void main(String[] args) throws SchedulerException {
//根据调度器工厂创建任务调度器实例
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//通过任务构建器对象创建任务实例
JobDetail jobdetail = JobBuilder.newJob(HelloJob.class).withIdentity("myjobdetail").build();
JobDataMap jobDataMap = jobdetail.getJobDataMap();
jobDataMap.put("key1","value1");
jobDataMap.put("key2","value2");
//通过触发器构建对象创建触发器实例
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("mytrigger")
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).repeatForever())
.build();
// 关联任务实例和触发器实例
scheduler.scheduleJob(jobdetail,trigger);
scheduler.start();
}
}
1.4 springboot集成
- 依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.6</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3.1</version>
</dependency>
<!--quartz 定时任务-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
<exclusions>
<exclusion>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
</exclusion>
<exclusion>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP-java6</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.5.2</version>
</plugin>
</plugins>
</build>
- 配置类
/**
* 定时任务配置
*
* @author
*/
@Configuration
public class ScheduleConfig {
//调度器工厂,用于创建调度器对象Scheduler
@Bean
public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setDataSource(dataSource);
//quartz参数
Properties prop = new Properties();
prop.put("org.quartz.scheduler.instanceName", "WitlinkedScheduler");
prop.put("org.quartz.scheduler.instanceId", "AUTO");
//线程池配置
prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
prop.put("org.quartz.threadPool.threadCount", "20");
prop.put("org.quartz.threadPool.threadPriority", "5");
//JobStore配置
prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
//集群配置
prop.put("org.quartz.jobStore.isClustered", "true");
prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
prop.put("org.quartz.jobStore.misfireThreshold", "12000");
prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");
factory.setQuartzProperties(prop);
factory.setSchedulerName("pdScheduler");
//延时启动
factory.setStartupDelay(3);
factory.setApplicationContextSchedulerContextKey("applicationContextKey");
factory.setOverwriteExistingJobs(true);
//设置自动启动,默认为true
factory.setAutoStartup(true);
return factory;
}
}
@Slf4j
@Configuration
@EnableSwagger2
public class ConfigurationSupport extends WebMvcConfigurationSupport {
@Bean
public Docket createRestApi() {
// 文档类型
return new Docket(DocumentationType.SWAGGER_2)
// 创建api的基本信息
.apiInfo(apiInfo())
// 选择哪些接口去暴露
.select()
// 扫描的包
.apis(RequestHandlerSelectors.basePackage("com.xing.quartz.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Swagger文档")
.version("1.0")
.build();
}
/**
* 防止@EnableMvc把默认的静态资源路径覆盖了,手动设置的方式
*
* @param registry
*/
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
// 解决静态资源无法访问
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
// 解决swagger无法访问
registry.addResourceHandler("/swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
// 解决swagger的js文件无法访问
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
- controller
@RestController
@RequestMapping("/api/quartzController")
public class QuartzController {
@Autowired
private Scheduler scheduler;
/**
* 添加定时任务
* @param jobId
* @param cronExpression
* @return
* @throws SchedulerException
*/
@PostMapping("/save")
@ApiOperation(value = "添加定时任务")
public String save(String jobId, String cronExpression) throws SchedulerException {
//创建任务实例
JobDetail jobDetail = JobBuilder.newJob(JobOne.class).withIdentity(jobId).build();
jobDetail.getJobDataMap().put("jobId",jobId);
//创建触发器对象
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(jobId)
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing())
.build();
scheduler.scheduleJob(jobDetail, trigger);
return "OK";
}
/**
* 暂停定时任务
* @param jobId
* @return
* @throws SchedulerException
*/
@PutMapping("/pause/{jobId}")
@ApiOperation(value ="暂停定时任务")
public String pause(@PathVariable("jobId") String jobId) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(jobId);
scheduler.pauseJob(jobKey);
return "OK";
}
/**
* 暂停所有定时任务
* @return
* @throws SchedulerException
*/
@PutMapping("/pauseAll")
@ApiOperation(value ="暂停所有定时任务")
public String pauseAll() throws SchedulerException {
scheduler.pauseAll();
return "OK";
}
/**
* 恢复定时任务
* @param jobId
* @return
* @throws SchedulerException
*/
@PutMapping("/resume/{jobId}")
@ApiOperation(value ="恢复定时任务")
public String resume(@PathVariable("jobId") String jobId) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(jobId);
scheduler.resumeJob(jobKey);
return "OK";
}
/**
* 恢复所有定时任务
* @return
* @throws SchedulerException
*/
@PutMapping("/resumeAll")
@ApiOperation(value ="恢复所有定时任务")
public String resumeAll() throws SchedulerException {
scheduler.resumeAll();
return "OK";
}
/**
* 删除定时任务
* @param jobId
* @return
* @throws SchedulerException
*/
@DeleteMapping("/delete/{jobId}")
@ApiOperation(value ="删除定时任务")
public String delete(@PathVariable("jobId") String jobId) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(jobId);
scheduler.deleteJob(jobKey);
return "OK";
}
/**
* 删除所有定时任务
* @return
* @throws SchedulerException
*/
@DeleteMapping("/delete")
@ApiOperation(value ="删除所有定时任务")
public String deleteAll() throws SchedulerException {
return "OK";
}
/**
* 立即执行定时任务
* @return
* @throws SchedulerException
*/
@PutMapping("/run/{jobId}")
@ApiOperation(value ="立即执行定时任务")
public String run(@PathVariable("jobId")String jobId) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(jobId);
scheduler.triggerJob(jobKey);
return "OK";
}
@PutMapping("/update/{jobId}")
@ApiOperation(value = "更新定时任务")
public String update(@PathVariable()String jobId,String cronExpression) throws SchedulerException {
//触发器唯一标识
TriggerKey triggerKey = TriggerKey.triggerKey(jobId);
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing();
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
trigger = trigger.getTriggerBuilder().withIdentity(jobId).withSchedule(cronScheduleBuilder).build();
scheduler.rescheduleJob(triggerKey, trigger);
return "OK" ;
}
@GetMapping("/findAll")
@ApiOperation(value = "查看所有任务")
public Map<String,Object> findAll() throws SchedulerException {
HashMap<String, Object> map = new HashMap<>();
List<String> triggerGroupNames = scheduler.getTriggerGroupNames();
//再获取Scheduler下的所有group
for (String triggerGroupName : triggerGroupNames) {
//组装group的匹配,为了模糊获取所有的triggerKey或者jobKey
GroupMatcher groupMatcher = GroupMatcher.groupEquals(triggerGroupName);
Set<TriggerKey> triggerKeys = scheduler.getTriggerKeys(groupMatcher);
for (TriggerKey triggerKey : triggerKeys) {
Trigger trigger = scheduler.getTrigger(triggerKey);
JobKey jobKey = trigger.getJobKey();
map.put(triggerKey.getName(),jobKey);
}
}
return map;
}
}