分布式定时任务 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:触发器的基本信息。
配置文件
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自动配置改造
-
去除
QuartzSchedulerConfig
配置类 -
maven依赖
<!--spring boot集成quartz-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
- 添加
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