樣例介紹
采用quartz,并使用數(shù)據(jù)庫來動態(tài)管理定時任務(wù)月幌。
步驟
(1)導(dǎo)入依賴
<!--quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!--數(shù)據(jù)庫 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
(2)添加配置
數(shù)據(jù)庫配置:
spring.datasource.druid.url=jdbc:mysql://*.*.*.*(這里使用自己服務(wù)器的ip):3306/(自己的數(shù)據(jù)庫)?reWriteBatchedInserts=true
spring.datasource.druid.username=root
spring.datasource.druid.password=123456
spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver
quartz配置:
#--------------------------------------------------------------
#調(diào)度器屬性
#--------------------------------------------------------------
# 調(diào)度器實例名:可以隨意命名
org.quartz.scheduler.instanceName=quartz-demo
# 實例ID: 允許隨意命名策菜,但必須保持唯一,集群可以讓quartz來幫你命名,設(shè)置為AUTO
org.quartz.scheduler.instanceId=AUTO
#只有org.quartz.scheduler.instanceId設(shè)置為“AUTO”才使用剖踊。
# 默認為“org.quartz.simpl.SimpleInstanceIdGenerator”驼鞭,它是主機名和時間戳生成實例Id的秦驯。
# 其它的IntanceIdGenerator實現(xiàn)包括SystemPropertyInstanceIdGenerator(它從系統(tǒng)屬性“org.quartz.scheduler.instanceId”獲取實例Id),
# 和HostnameInstanceIdGenerator(它使用本地主機名InetAddress.getLocalHost().getHostName()生成實例Id)挣棕。
# 你也實現(xiàn)你自己的InstanceIdGenerator
org.quartz.scheduler.instanceIdGenerator.class=com.yw.quartzdemo.support.QuartzInstanceIdGenerator
#--------------------------------------------------------------
# 線程池屬性
#--------------------------------------------------------------
# org.quartz.threadPool.class這個值是一個實現(xiàn)了 org.quartz.spi.ThreadPool 接口的類的全限名稱译隘。
# Quartz 自帶的線程池實現(xiàn)類是 org.quartz.smpl.SimpleThreadPool,
# 它能夠滿足大多數(shù)用戶的需求洛心。這個線程池實現(xiàn)具備簡單的行為固耘,并經(jīng)很好的測試過。
# 它在調(diào)度器的生命周期中提供固定大小的線程池词身。
# 你能根據(jù)需求創(chuàng)建自己的線程池實現(xiàn)厅目,如果你想要一個隨需可伸縮的線程池時也許需要這么做。
# 這個屬性沒有默認值法严,你必須為其指定值损敷。
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
# threadCount 屬性控制了多少個工作者線程被創(chuàng)建用來處理 Job。
# 原則上是深啤,要處理的 Job 越多拗馒,那么需要的工作者線程也就越多。
# threadCount 的數(shù)值至少為 1墓塌。
# Quartz 沒有限定你設(shè)置工作者線程的最大值瘟忱,但是在多數(shù)機器上設(shè)置該值超過100的話就會顯得相當(dāng)不實用了奥额,
# 特別是在你的 Job 執(zhí)行時間較長的情況下。
# 這項沒有默認值访诱,所以你必須為這個屬性設(shè)定一個值
org.quartz.threadPool.threadCount=10
# 優(yōu)先級:可以是Thread.MIN_PRIORITY (1)和Thread.MAX_PRIORITY (10)之間的任意整數(shù)垫挨。默認為Thread.NORM_PRIORITY (5).
org.quartz.threadPool.threadPriority=5
# 繼承初始化線程的上下文類加載器線程
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
#--------------------------------------------------------------
#作業(yè)存儲設(shè)置:作業(yè)存儲部分的設(shè)置描述了在調(diào)度器實例的生命周期中,Job 和 Trigger 信息是如何被存儲的触菜。
# 把調(diào)度器信息存儲在內(nèi)存中非常的快也易于配置九榔。
# 當(dāng)調(diào)度器進程一旦被終止,所有的 Job 和 Trigger 的狀態(tài)就丟失了涡相。
# 要使 Job 存儲在內(nèi)存中需通過設(shè)置 org.quartz.jobStrore.class 屬性為 org.quartz.simpl.RAMJobStore哲泊,
# 在Cron Trigger 和“作業(yè)存儲和持久化”會用到的不同類型的作業(yè)存儲實現(xiàn)。
#--------------------------------------------------------------
# trigger被認為失敗之前催蝗,scheduler能夠承受的下一次觸發(fā)時間(單位毫秒)切威。默認值為60秒。
org.quartz.jobStore.misfireThreshold=60000
# 用于將調(diào)度信息(job丙号、trigger和calendar)存儲到關(guān)系數(shù)據(jù)庫中先朦。
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
# 設(shè)置當(dāng)前數(shù)據(jù)庫Driver代理的數(shù)據(jù)庫系統(tǒng)的方言
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
# 表前綴
org.quartz.jobStore.tablePrefix=QRTZ_
# JobStore處理失敗trigger的最大等待時間。
# 同時處理多個trigger(多于幾個)回引發(fā)數(shù)據(jù)表長時間鎖定犬缨,觸發(fā)其它的trigger(還沒有失斣骸)的性能就會受到限制
org.quartz.jobStore.maxMisfiresToHandleAtATime=10
# 使用集群特性,這個屬性必須為true怀薛。
# 如果你有多個Quartz實例使用相同的數(shù)據(jù)庫表刺彩,這個屬性必須為true,否則你會體驗一把大破壞枝恋。參見集群配置创倔。
org.quartz.jobStore.isClustered=true
# 設(shè)置當(dāng)前實例check in集群中其它實例的頻率。影響檢測到故障實例的速度
org.quartz.jobStore.clusterCheckinInterval=20000
# “org.quartz.jobStore.useProperties”配置參數(shù)可以被設(shè)置為true(默認為false)焚碌,
# 這樣可以指導(dǎo)JDBCJobStore三幻,JobDataMaps中的值都是字符串,因此這樣可以以名字-值對存儲呐能,
# 而不是存儲更加復(fù)雜的對象(序列化形式BLOB)。
# 從長遠來看抑堡,這是很安全的摆出,因為避免了將非字符串類序列化為BLOB的類版本問題
org.quartz.jobStore.useProperties=true
(3)添加定時任務(wù)表的PO,mapper,dao
po:
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("JOB_DEF")
@Builder
public class JobDefPO {
/**
* 任務(wù)名稱-類全名
*/
private String jobName;
/**
* 任務(wù)分組
*/
private String jobGroup;
/**
* 任務(wù)執(zhí)行表達式
*/
private String cron;
/**
* 任務(wù)狀態(tài)
*/
private Integer status;
}
mapper:
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yw.quartzdemo.po.JobDefPO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface JobDefMapper extends BaseMapper<JobDefPO> {
}
dao
import com.yw.quartzdemo.po.JobDefPO;
import java.util.List;
/**
* 任務(wù)定義接口
*/
public interface JobDefDAO {
/***
* @Description 查詢所有任務(wù)
* @author yuanwei
* @param
* @return java.util.List<com.yw.quartzdemo.po.JobDefPO>
* @time 2021/1/6 16:35
*/
List<JobDefPO> listAll();
}
dao.impl:
import com.baomidou.mybatisplus.core.conditions.Condition;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yw.quartzdemo.dao.JobDefDAO;
import com.yw.quartzdemo.mapper.JobDefMapper;
import com.yw.quartzdemo.po.JobDefPO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 任務(wù)定義實現(xiàn)
*/
@Service
@Slf4j
public class JobDefDAOImpl extends ServiceImpl<JobDefMapper, JobDefPO> implements JobDefDAO {
@Override
public List<JobDefPO> listAll() {
List<JobDefPO> result = null;
final QueryWrapper<JobDefPO> wrapper = Condition.create();
try {
result = this.list(wrapper);
} catch (final Exception e) {
log.error("查詢定時任務(wù)定義異常:{},{}", e.getMessage(), e);
throw new RuntimeException("查詢定時任務(wù)定義失敗");
}
return result;
}
}
(4)添加配置:
config包下
quartzConfig.java
import lombok.extern.slf4j.Slf4j;
import org.quartz.Scheduler;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.Properties;
@Configuration
@Slf4j
public class QuartzConfig {
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
@Autowired
private DataSource dataSource;
public Properties quartzProperties() throws IOException {
final PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("quartz.properties"));
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
@Bean(name = "quartzJobFactory")
public AdaptableJobFactory quartzJobFactory() {
return new AdaptableJobFactory() {
@Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object jobInstance = super.createJobInstance(bundle);
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
};
}
@Bean(name = "schedulerFactoryBean")
public SchedulerFactoryBean schedulerFactoryBean(@Qualifier("quartzJobFactory") final AdaptableJobFactory quartzJobFactory) {
final SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
try {
schedulerFactoryBean.setQuartzProperties(quartzProperties());
schedulerFactoryBean.setDataSource(dataSource);
schedulerFactoryBean.setJobFactory(quartzJobFactory);
schedulerFactoryBean.setStartupDelay(5);
schedulerFactoryBean.setOverwriteExistingJobs(true);
} catch (final IOException ex) {
log.error(ex.getMessage(), ex);
throw new RuntimeException("定時任務(wù)調(diào)度實體構(gòu)建異常");
}
return schedulerFactoryBean;
}
@Bean(name = "scheduler")
public Scheduler scheduler(@Qualifier("schedulerFactoryBean") final SchedulerFactoryBean schedulerFactoryBean) {
return schedulerFactoryBean.getScheduler();
}
}
supper包下:
JobRefresh.java 用于設(shè)置定時刷新頻率首妖。
import com.yw.quartzdemo.dao.JobDefDAO;
import com.yw.quartzdemo.po.JobDefPO;
import lombok.extern.slf4j.Slf4j;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.List;
/**
* @description
* @author yuanwei
* @date 2021/1/6 11:17
*/
@Component
@Slf4j
public class JobRefresh {
@Autowired
private JobDefDAO jobDefDAO;
@Autowired
private Scheduler scheduler;
@SuppressWarnings("unchecked")
@Scheduled(cron = "0 0 1 * * ?") // 每天凌晨1點刷新一次
@PostConstruct // 應(yīng)用啟動時刷新一次偎漫,方便測試,后續(xù)可以去掉
public void refreshTrigger() throws SchedulerException {
final List<JobDefPO> jobDefs = jobDefDAO.listAll();
for (final JobDefPO jobDef : jobDefs) {
final TriggerKey triggerKey = TriggerKey.triggerKey(jobDef.getJobName(), jobDef.getJobGroup());
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
if (null == trigger) {
// 狀態(tài)(0:正常有缆,1:禁用)
if (jobDef.getStatus().intValue() == 1) {
continue;
}
JobDetail jobDetail = null;
try {
jobDetail = JobBuilder.newJob((Class<? extends Job>) Class.forName(jobDef.getJobName()))
.withIdentity(jobDef.getJobName(), jobDef.getJobGroup()).build();
} catch (final ClassNotFoundException ex) {
log.error(ex.getMessage(), ex);
throw new RuntimeException("刷新任務(wù)異常");
}
final CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(jobDef.getCron());
trigger = TriggerBuilder.newTrigger().withIdentity(jobDef.getJobName(), jobDef.getJobGroup())
.withSchedule(scheduleBuilder).build();
scheduler.scheduleJob(jobDetail, trigger);
} else {
if (jobDef.getStatus().intValue() == 1) {
final JobKey jobKey = JobKey.jobKey(jobDef.getJobName(), jobDef.getJobGroup());
scheduler.deleteJob(jobKey);
continue;
}
final String selectedCron = jobDef.getCron();
final String currentCron = trigger.getCronExpression();
if (!selectedCron.equals(currentCron)) {
final CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(selectedCron);
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder)
.build();
scheduler.rescheduleJob(triggerKey, trigger);
}
}
}
}
}
(5)添加定時任務(wù)
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.util.Date;
/**
* @description
* @author yuanwei
* @date 2021/1/6 16:38
*/
@Slf4j
public class DemoJobHandler implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
Date time = new Date();
log.info("時間:{}象踊,打印",time);
}
}
(6)在數(shù)據(jù)庫中添加(5)中任務(wù)的觸發(fā)時間
其中status中0為開啟温亲,1為關(guān)閉。
INSERT INTO `fund`.`JOB_DEF`(`JOB_NAME`, `JOB_GROUP`, `CRON`, `STATUS`) VALUES ('com.yw.quartzdemo.handler.DemoJobHandler', 'demo', '* * * * * ?', 0);
github鏈接:https://github.com/source201/yw-doc