本文完成了springboot2.0与调度工具quartz的集成,完成高可用,将状态保存到数据initialize-schema: never这个有三个值,有兴趣的可以去查一下,是关于在数据库中创建quartz表的配置
在项目启动时,将配置文件的job加入到调度服务里面,同时调度服务把它记录到数据库中,然后任务在触发点执行,调度服务回去修改相应状态,通过数据库锁的机制,完成分布式高可用
1.pom.xml依赖加入
<!-- quartz -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
2.添加application.yml文件配置
spring:
profiles:
include:
- quartz
datasource:
driverClassName: com.mysql.jdbc.Driver
url:jdbc:mysql://localhost:3306/demoallowMultiQueries=true&autoReconnect=true
username: root
pssword: root
type: com.alibaba.druid.pool.DruidDataSource
maxActive: 20
initialSize: 1
maxWait: 60000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxOpenPreparedStatements: 20
filters: stat,wall,log4j
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=50
druid:
stat-view-servlet:
enabled: true
url-pattern: /druid/*
login-username: admin
login-password: aaa111
quartz:
jdbc:
initialize-schema: never
job-store-type: jdbc
#相关属性配置
properties:
org:
quartz:
scheduler:
instanceName: DICMP_PARTNER_JOB
instanceId: AUTO
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
3.新建application-quartz.yml文件
4.编写quartz配置类
@Component
@Data
@ConfigurationProperties
public class ScheduleJob implements Serializable {
public static final String STATUS_RUNNING = "1";
public static final String STATUS_NOT_RUNNING = "0";
/**
* 任务名称
*/
private String jobName;
/**
* 任务分组
*/
private String jobGroup;
/**
* 任务状态 是否启动任务
*/
private String jobStatus;
/**
* cron表达式
*/
private String cronExpression;
/**
* spring bean
*/
private String jobClass;
/**
* 任务调用的方法名
*/
private String methodName;
/**
* 任务调用的方法传入的参数,统一使用String
*/
private Map parameters;
/**
* 任务是否有状态
*/
private boolean isConcurrent;
}
@Data
@ConfigurationProperties(prefix = "quartz")
@Component
public class QuartzConfig {
private List<ScheduleJob> jobs = new ArrayList<ScheduleJob>();
}
5.编写quartz执行入口
/**
不允许并行执行
*/
@DisallowConcurrentExecution
public class QuartzJobFactoryDisallowConcurrentExecution implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
ScheduleJob scheduleJob = (ScheduleJob) context.getMergedJobDataMap().get("scheduleJob");
TaskUtils.invokeMethod(scheduleJob);
}
}
/** 允许并行执行 */
public class QuartzJobFactoryAllowConcurrentExecution implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
ScheduleJob scheduleJob = (ScheduleJob) context.getMergedJobDataMap().get("scheduleJob");
TaskUtils.invokeMethod(scheduleJob);
}
}
6.编写quartz添加job调度类
@Component
public class ScheduleJobFactory {
public final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private SchedulerFactoryBean schedulerFactoryBean;
@Autowired
private QuartzConfig quartzConfig;
/**
* 初始化任务
* @param job
*/
public void initJob(ScheduleJob job) throws Exception {
if (job == null) {
return;
}
Scheduler scheduler = schedulerFactoryBean.getScheduler();
TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());
if (!ScheduleJob.STATUS_RUNNING.equals(job.getJobStatus())) {
logger.info("删除任务{}",job.getJobName());
this.deleteJob(scheduler,triggerKey);
} else {
logger.info(scheduler + "添加任务:{}", JSONObject.toJSONString(job));
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
addJob(scheduler,triggerKey,job);
}
}
/**
* 增加定时任务job
* @param scheduler 调度器
* @param triggerKey 触发器key
* @param job 任务
* @throws Exception
*/
private void addJob(Scheduler scheduler,TriggerKey triggerKey,ScheduleJob job) throws Exception {
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
if (null == trigger) {
// 不存在,创建一个
setJobExistTrigger(scheduler,trigger,triggerKey,job);
} else {
setJobNoExistTrigger(scheduler,trigger,triggerKey,job);
}
}
/**
* 如果触发器key不存在,设置job
* @param scheduler 调度器
* @param trigger 触发器
* @param triggerKey 触发器key
* @param job 任务
* @throws Exception
*/
private void setJobNoExistTrigger(Scheduler scheduler,CronTrigger trigger,TriggerKey triggerKey,ScheduleJob job)
throws Exception {
// Trigger已存在,那么更新相应的定时设置
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
// 按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
// 按新的trigger重新设置job执行
scheduler.rescheduleJob(triggerKey, trigger);
}
/**
* 如果触发器key存在,设置job
* @param scheduler 调度器
* @param trigger 触发器
* @param triggerKey 触发器key
* @param job 任务
* @throws Exception
*/
private void setJobExistTrigger(Scheduler scheduler,CronTrigger trigger,TriggerKey triggerKey,ScheduleJob job)
throws Exception{
Class clazz = job.isConcurrent() ? QuartzJobFactoryAllowConcurrentExecution.class
: QuartzJobFactoryDisallowConcurrentExecution.class;
JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(job.getJobName(), job.getJobGroup()).build();
jobDetail.getJobDataMap().put("scheduleJob", job);
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup())
.withSchedule(scheduleBuilder).build();
scheduler.scheduleJob(jobDetail, trigger);
}
/**
* 删除job
* @param scheduler
* @param triggerKey
* @throws Exception
*/
private void deleteJob(Scheduler scheduler,TriggerKey triggerKey) throws Exception{
if (null != triggerKey) {
scheduler.unscheduleJob(triggerKey);
}
}
/**
* 初始化job
* @throws Exception
*/
@PostConstruct
public void init() {
// 这里获取任务信息数据
List<ScheduleJob> jobList = quartzConfig.getJobs();
try {
logger.info("任务初始化开始....");
for (ScheduleJob job : jobList) {
initJob(job);
}
} catch (Exception e) {
logger.error("任务初始化异常",e);
}
}
}
7.反射工具类
public class TaskUtils {
private static final Logger logger = LoggerFactory.getLogger(TaskUtils.class);
/**
* 通过反射调用scheduleJob中定义的方法
* @param scheduleJob
*/
public static void invokeMethod(ScheduleJob scheduleJob) {
Object object = null;
Class clazz;
if (StringUtils.isNotBlank(scheduleJob.getJobClass())) {
object = SpringUtil.getBean(scheduleJob.getJobClass());
}
if (object == null) {
logger.error("任务名称 = [" + scheduleJob.getJobName() + "]未启动成功,请检查是否配置正确!!!");
return;
}
clazz = object.getClass();
Method method = null;
try {
if (scheduleJob.getParameters() == null) {
method = clazz.getDeclaredMethod(scheduleJob.getMethodName());
method.invoke(object);
} else {
method = clazz.getDeclaredMethod(scheduleJob.getMethodName(), Map.class);
method.invoke(object, scheduleJob.getParameters());
}
} catch (Exception e) {
logger.error("job反射执行方法异常",e);
}
}
@Component
public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (SpringUtil.applicationContext == null) {
SpringUtil.applicationContext = applicationContext;
}
}
/**
* 获取applicationContext
* @return
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 通过name获取 Bean.
* @param name
* @return
*/
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
/**
* 通过class获取Bean.
* @param clazz
* @param <T>
* @return
*/
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
/**
* 通过name,以及Clazz返回指定的Bean
* @param name
* @param clazz
* @param <T>
* @return
*/
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
8.适应多个服务,需要多个调度器,修改调度器名称