文章目录
- 前言
- 一、Quartz是什么?
- 难易程度
- 二、核心思想
- 三、使用步骤
- 1.引入依赖
- 2.创建相应类
- 总结
前言
引用上一篇博客的说法 springboot整合xxl-job ,集群模式下,定时任务会造成很严重的事故,其次普通的任务也无法做到像xxl-job与Quartz 一样,及时启停,修改等;上一篇介绍了xxl-job,这篇搞一下Quartz,老牌分布式定时任务了;
一、Quartz是什么?
比较详细的Quartz文档
难易程度
Quartz 并没有给出明确的例子,但是文档详细,且任务的操作简单明了,步骤清晰,本身是可以脱离Spring-boot 单独工作的,而且可以更高度自定义话。需要自己摸搜整合,一般还需要自己维护数据库的表等,为了任务的添加做参数等;
xxl-job 官方提供了springboot以及普通项目集成demo,所以在集成方面,只要理解了它的思想xxl-job集成很快,数据库等都是现有的,导入即可;但是封装较深,尤其分为了服务端与客户端,无形增加了服务数量,且通信之间还有可能有通信信息丢失情况等;
二、核心思想
- Quartz 的调度中心是Scheduler,所以只要将任务交给它就行了
- 一个通用的任务是JobDetail,用来接受各种具体的任务逻辑
- CronTrigger 是JobDetail 的触发条件,所以Scheduler需要的是 JobDetail 任务和CronTrigger 任务触发器
- JobDetail既然是通用任务,用于接受任务,所以我们要定义一个自己的任务类(例如叫做QuartzJob),这个任务类需要实现 Job接口
- 这个任务类QuartzJob,要执行具体的任务,具体的任务,一般都是我们需要自己的一些方法,
那么QuartzJob中其实就要执行LogImpl 这个类中的 clear()这个方法,这里通过反射执行;例如: 每天晚上12点执行一次清理数据库之前的数据; 一般我们会写一个这样的方法 public class LogImpl implements LogService{ public void clear(){ logService.remove(new Date()); } }
6. 因为我们一般都会在数据库中加入 LogImpl 这个类中的 clear()这个方法,然后交给Quartz执行,所以只能通过反射执行
三、使用步骤
1.引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2.创建相应类
- QuartzUtil 此类作为操作job的工具类,这里的job与quartz中的 JobDetail一一对应 ,包括任务的增删改
public class QuartzUtil {
/**
* 将新增的任务放入quartz调度中心中执行
*
* @param sysJob 具体任务信息
* @throws Exception 异常
*/
@SneakyThrows
public static void insertOrUpdateJob(SysJob sysJob) {
// 获取调度器 Scheduler
Scheduler scheduler = SchedulerStatic.getScheduler();
Long jobId = sysJob.getJobId();
String jobGroup = sysJob.getJobGroup();
// 构造一个job
JobKey jobKey = JobKey.jobKey(jobId.toString(), jobGroup);
JobDetail jobDetail = JobBuilder
.newJob(QuartzJob.class)
.withIdentity(jobKey)
.build();
// 构造cron调度器
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(sysJob.getCronExpression());
getMisfirePolicy(sysJob, cronScheduleBuilder);
// 构造触发器 trigger
TriggerKey triggerKey = TriggerKey.triggerKey(jobId.toString(), jobGroup);
CronTrigger trigger = TriggerBuilder
.newTrigger()
.withIdentity(triggerKey)
.withSchedule(cronScheduleBuilder)
.build();
// 放入job信息,为后续执行改任务的具体方法做铺垫(需要反射调用), 在execute中获取并应用
jobDetail.getJobDataMap().put(QuartzEnum.jobKey, sysJob);
// 判断该任务是否存在,修改任务,先删除然后添加
if (scheduler.checkExists(jobKey)) {
// 防止创建时存在数据问题 先移除,然后在执行创建操作
scheduler.deleteJob(jobKey);
}
// 判断任务是否过期
CronExpression cron = new CronExpression(sysJob.getCronExpression());
Date nextValidTimeAfter = cron.getNextValidTimeAfter(new Date(System.currentTimeMillis()));
if (!ObjectUtils.isEmpty(nextValidTimeAfter)) {
// 执行调度任务
scheduler.scheduleJob(jobDetail, trigger);
}
// 暂停任务
if (sysJob.getStatus().equals(QuartzEnum.PAUSE)) {
scheduler.pauseJob(jobKey);
}
}
/**
* 根据计划执行错误策略,设置调度器
*
* @param sysJob 任务信息
* @param cronScheduleBuilder cron调度器
*/
private static void getMisfirePolicy(SysJob sysJob, CronScheduleBuilder cronScheduleBuilder) {
switch (sysJob.getMisfirePolicy()) {
case QuartzEnum.MISFIRE_DEFAULT:
break;
case QuartzEnum.MISFIRE_IGNORE_MISFIRES:
cronScheduleBuilder.withMisfireHandlingInstructionIgnoreMisfires();
break;
case QuartzEnum.MISFIRE_FIRE_AND_PROCEED:
cronScheduleBuilder.withMisfireHandlingInstructionFireAndProceed();
break;
case QuartzEnum.MISFIRE_DO_NOTHING:
cronScheduleBuilder.withMisfireHandlingInstructionDoNothing();
break;
default:
throw new RuntimeException("The task misfire policy '" + sysJob.getMisfirePolicy() + "' cannot be used in cron schedule tasks");
}
}
/**
* 删除任务
*
* @param sysJob 具体任务信息(获取jobkey)
*/
@SneakyThrows
public static void deleteJob(SysJob sysJob) {
// 获取调度器 Scheduler
Scheduler scheduler = SchedulerStatic.getScheduler();
scheduler.deleteJob(JobKey.jobKey(sysJob.getJobId().toString(), sysJob.getJobGroup()));
}
}
- QuartzJob
-
- 此类作为所有任务的执行入口,其他要执行的具体逻辑应该注入spring,然后通过此 通过反射调用到具体任务执行类
-
- sysJob包含着要执行的具体逻辑类的信息,以及方法,参数等信息,此案例维护在数据库中
-
- 通过更改数据库数据,动态变更quartz中的任务
-
- 目前仅仅做了无参构造器的方法执行,后续补充
public class QuartzJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 获取任务执行参数
SysJob sysJob = (SysJob) context.getJobDetail().getJobDataMap().get(QuartzEnum.jobKey);
// invokeTarget 应该符合 类.方法名 例如 task.sout()
String invokeTarget = sysJob.getInvokeTarget();
String beanName = invokeTarget.split("\\.")[0];
String temp = invokeTarget.split("\\.")[1];
String methodName = temp.substring(0, temp.indexOf("("));
Object bean = SpringUtil.getBean(beanName);
Method method = null;
try {
method = bean.getClass().getMethod(methodName);
method.invoke(bean);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
- SchedulerStatic 此类仅仅作为引入静态调度器的工具类
@Component
public class SchedulerStatic {
private static Scheduler scheduler;
@Autowired
public SchedulerStatic(Scheduler scheduler) {
SchedulerStatic.scheduler = scheduler;
}
public static Scheduler getScheduler() {
return scheduler;
}
}
- MyTask
. 此类作为具体执行任务的示例类
- 此类应该注入spring容器中,为了后去获取方便,且一般业务都需要查询数据,所以此类比较常见
- 如果非bean,那么 {@link com.example.springbootquartz.quartz.QuartzJob}.中需要自己扩展获取目标类 利用Class.forName()
@Service("task")
public class MyTask {
@Autowired
SysJobService sysJobService;
/**
* 任务的具体执行逻辑写在此处,这里只做打印
*/
public void sout() {
List<SysJob> list = sysJobService.list();
list.forEach(sysJob -> System.out.println(sysJob.toString()));
}
}
- yml 配置文件
集成了swagger mp mysql quartz等
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/quartz?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowMultiQueries=true&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
username: root
password: root
# 页面配置
mvc:
path match:
matching-strategy: ant_path_matcher
server:
port: 8889
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
- 通过接口文档地址,操作任务,达到动态更改分布式任务的效果
- 直接访问控制台打印的地址即可
- 通过添加任务的详细信息,查看任务执行情况
可以看到,已经开始执行任务了,0/2 * * * * ? 每两秒后执行一次
执行的是打印
总结
基本实现了任务的动态添加
后续完善:
- 有参数的具体任务调用,修改QuartzJob中的方法获取,参数获取等,但是依旧采用代理实现
- 任务并发执行未处理,后续完善
项目地址: springboot-quartz