献县网站建设,云南科技网站建设,宁波网络营销服务,单页网站怎么做竞价前言
本文主要介绍了SpringBoot架构下动态定时任务的使用#xff0c;定时任务表达式配置在数据库中#xff0c;通过反射执行到目标方法。
Quartz
Quartz 是一个开源的作业调度框架,支持分布式定时任务#xff0c;Quartz定时任务据我了解可分为Trigger#xff08;触发器定时任务表达式配置在数据库中通过反射执行到目标方法。
Quartz
Quartz 是一个开源的作业调度框架,支持分布式定时任务Quartz定时任务据我了解可分为Trigger触发器、Job任务和Scheduler调度器定时任务的逻辑大体为创建触发器和任务并将其加入到调度器中。
Quartz 的核心类有以下三部分
任务 Job 需要实现的任务类实现 execute() 方法执行后完成任务; 触发器 Trigger 包括 SimpleTrigger 和 CronTrigger; 调度器 Scheduler 任务调度器负责基于 Trigger触发器来执行 Job任务.
Trigger 有五种触发器 SimpleTrigger 触发器需要在特定的日期/时间启动且以指定的间隔时间单位毫秒重复执行 n 次任务如 在 9:00 开始每隔1小时每隔几分钟每隔几秒钟执行一次 。没办法指定每隔一个月执行一次每月的时间间隔不是固定值。 CalendarIntervalTrigger 触发器指定从某一个时间开始以一定的时间间隔单位有秒分钟小时天月年星期执行的任务。 DailyTimeIntervalTrigger 触发器指定每天的某个时间段内以一定的时间间隔执行任务。并且支持指定星期。如指定每天 9:00 至 18:00 每隔 70 秒执行一次并且只要周一至周五执行。 CronTrigger 触发器基于日历的任务调度器即指定星期、日期的某时间执行任务。 NthIncludedDayTrigger 触发器不同时间间隔的第 n 天执行任务。比如在每个月的第 15 日处理财务发票记帐同样设定双休日或者假期。
创建任务表
create table sys_job (job_id bigint(20) not null auto_increment comment 任务ID,job_name varchar(64) default comment 任务名称,job_group varchar(64) default DEFAULT comment 任务组名,invoke_target varchar(500) not null comment 调用目标方法,cron_expression varchar(255) default comment cron执行表达式,misfire_policy varchar(20) default 3 comment 计划执行错误策略1立即执行 2执行一次 3放弃执行,concurrent char(1) default 1 comment 是否并发执行0允许 1禁止,status char(1) default 0 comment 状态0正常 1暂停,create_by varchar(64) default comment 创建者,create_time datetime comment 创建时间,update_by varchar(64) default comment 更新者,update_time datetime comment 更新时间,remark varchar(500) default comment 备注信息,primary key (job_id, job_name, job_group)
) engineinnodb auto_increment100 comment 定时任务调度表;INSERT INTO sys_job(job_id, job_name, job_group, invoke_target, cron_expression, misfire_policy, concurrent, status, create_by, create_time, update_by, update_time, remark) VALUES (2, 系统默认有参, DEFAULT, com.demo.task.Task.testParams(\hello\), 0/15 * * * * ?, 3, 1, 0, admin, 2024-01-16 19:07:33, , NULL, );
INSERT INTO sys_job(job_id, job_name, job_group, invoke_target, cron_expression, misfire_policy, concurrent, status, create_by, create_time, update_by, update_time, remark) VALUES (3, 系统默认无参, DEFAULT, task.testNoParams(), 0/20 * * * * ?, 3, 1, 0, admin, 2024-01-16 19:07:33, , NULL, );create table sys_job_log (job_log_id bigint(20) not null auto_increment comment 任务日志ID,job_name varchar(64) not null comment 任务名称,job_group varchar(64) not null comment 任务组名,invoke_target varchar(500) not null comment 调用目标字符串,job_message varchar(500) comment 日志信息,status char(1) default 0 comment 执行状态0正常 1失败,exception_info varchar(2000) default comment 异常信息,create_time datetime comment 创建时间,primary key (job_log_id)
) engineinnodb comment 定时任务调度日志表;添加依赖
dependencygroupIdorg.apache.commons/groupIdartifactIdcommons-lang3/artifactId
/dependencydependencygroupIdorg.quartz-scheduler/groupIdartifactIdquartz/artifactId
/dependencydependencygroupIdcn.hutool/groupIdartifactIdhutool-all/artifactIdversion4.1.14/version
/dependency
dependencygroupIdorg.springframework/groupIdartifactIdspring-context-support/artifactId
/dependency定义Job
Quartz定时任务默认都是并发执行的不会等待上一次任务执行完毕只要间隔时间到就会执行, 如果定时任执行太长会长时间占用资源导致其它任务堵塞。
一般设置都是禁止并发执行
//禁止并发执行
DisallowConcurrentExecution
public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob {Overrideprotected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception {JobInvokeUtil.invokeMethod(sysJob);}
}public abstract class AbstractQuartzJob implements Job {private static final Logger log LoggerFactory.getLogger(AbstractQuartzJob.class);/*** 线程本地变量*/private static ThreadLocalDate threadLocal new ThreadLocal();Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {SysJob sysJob new SysJob();BeanUtils.copyProperties(context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES),sysJob);try {before(context, sysJob);if (sysJob ! null){doExecute(context, sysJob);}after(context, sysJob, null);}catch (Exception e){log.error(任务执行异常 - , e);after(context, sysJob, e);}}/*** 执行前** param context 工作执行上下文对象* param sysJob 系统计划任务*/protected void before(JobExecutionContext context, SysJob sysJob) {threadLocal.set(new Date());}/*** 执行后** param context 工作执行上下文对象* param sysJob 系统计划任务*/protected void after(JobExecutionContext context, SysJob sysJob, Exception e){Date startTime threadLocal.get();threadLocal.remove();// todo 写入数据库当中}/*** 执行方法由子类重载** param context 工作执行上下文对象* param sysJob 系统计划任务* throws Exception 执行过程中的异常*/protected abstract void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception;
}
实体类
Data
public class SysJob implements Serializable {private static final long serialVersionUID 1L;/** 任务ID */private Long jobId;/** 任务名称 */private String jobName;/** 任务组名 */private String jobGroup;/** 调用目标字符串 */private String invokeTarget;/** cron执行表达式 */private String cronExpression;/** cron计划策略 */// 0默认,1立即触发执行,2触发一次执行,3不触发立即执行private String misfirePolicy ScheduleConstants.MISFIRE_DEFAULT;/** 是否并发执行0允许 1禁止 */private String concurrent;/** 任务状态0正常 1暂停 */private String status;}创建定时任务 public static void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException {Class? extends Job jobClass getQuartzJobClass(job);// 构建job信息Long jobId job.getJobId();String jobGroup job.getJobGroup();JobDetail jobDetail JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build();// 表达式调度构建器CronScheduleBuilder cronScheduleBuilder CronScheduleBuilder.cronSchedule(job.getCronExpression());cronScheduleBuilder handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);// 按新的cronExpression表达式构建一个新的triggerCronTrigger trigger TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup)).withSchedule(cronScheduleBuilder).build();// 放入参数运行时的方法可以获取jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);// 判断是否存在if (scheduler.checkExists(getJobKey(jobId, jobGroup))){// 防止创建时存在数据问题 先移除然后在执行创建操作scheduler.deleteJob(getJobKey(jobId, jobGroup));}// 判断任务是否过期if (StringUtils.isNotNull(CronUtils.getNextExecution(job.getCronExpression()))){// 执行调度任务 核心代码scheduler.scheduleJob(jobDetail, trigger);}// 暂停任务if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())){scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));}}/*** 获取quartz任务类** param sysJob 执行计划* return 具体执行任务类*/private static Class? extends Job getQuartzJobClass(SysJob sysJob){boolean isConcurrent 0.equals(sysJob.getConcurrent());return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class;}反射类
public class JobInvokeUtil {/*** 执行方法** param sysJob 系统任务*/public static void invokeMethod(SysJob sysJob) throws Exception {String invokeTarget sysJob.getInvokeTarget();String beanName getBeanName(invokeTarget);String methodName getMethodName(invokeTarget);ListObject[] methodParams getMethodParams(invokeTarget);if (!isValidClassName(beanName)) {Object bean SpringUtils.getBean(beanName);invokeMethod(bean, methodName, methodParams);}else{Object bean Class.forName(beanName).getDeclaredConstructor().newInstance();invokeMethod(bean, methodName, methodParams);}}/*** 调用任务方法** param bean 目标对象* param methodName 方法名称* param methodParams 方法参数*/private static void invokeMethod(Object bean, String methodName, ListObject[] methodParams)throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,InvocationTargetException {if (StringUtils.isNotNull(methodParams) methodParams.size() 0) {Method method bean.getClass().getMethod(methodName, getMethodParamsType(methodParams));method.invoke(bean, getMethodParamsValue(methodParams));}else{Method method bean.getClass().getMethod(methodName);method.invoke(bean);}}}定时任务类
Component(task)
Slf4j
public class Task {public void testParams(String params) {log.info(执行有参方法 params);System.out.println();}public void testNoParams() {log.info(执行无参方法);}
}初始化定时任务 PostConstructpublic void init() throws SchedulerException, TaskException {scheduler.clear();ListSysJob jobList jobMapper.selectList(null);for (SysJob job : jobList) {ScheduleUtils.createScheduleJob(scheduler, job);}}运行效果 2024-03-25 14:05:30.020 INFO 11296 — [eduler_Worker-1] com.demo.task.Task : 执行有参方法hello 2024-03-25 14:05:40.005 INFO 11296 — [eduler_Worker-2] com.demo.task.Task : 执行无参方法 2024-03-25 14:05:45.008 INFO 11296 — [eduler_Worker-3] com.demo.task.Task : 执行有参方法hello 2024-03-25 14:06:00.012 INFO 11296 — [eduler_Worker-4] com.demo.task.Task : 执行有参方法hello 2024-03-25 14:06:00.014 INFO 11296 — [eduler_Worker-5] com.demo.task.Task : 执行无参方法 添加定时任务 public int insertJob(SysJob job) throws SchedulerException, TaskException {job.setStatus(ScheduleConstants.Status.PAUSE.getValue());int rows jobMapper.insert(job);if (rows 0) {ScheduleUtils.createScheduleJob(scheduler, job);}return rows;}解决 Quartz Job 中无法注入 Spring Bean
首先自定义一个 JobFactory通过 AutowireCapableBeanFactory 将创建好的 Job 对象交给 Spring 管理
Configuration
public class CustomJobFactory extends AdaptableJobFactory {Autowiredprivate AutowireCapableBeanFactory autowireCapableBeanFactory;/*** Create the job instance, populating it with property values taken* from the scheduler context, job data map and trigger data map.** param bundle*/Overrideprotected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {Object jobInstance super.createJobInstance(bundle);autowireCapableBeanFactory.autowireBean(jobInstance);return jobInstance;}
}再创建一个配置类将自定义的 JobFactory 设置到 Schedule 中
Configuration
public class QuartzConfig {Autowiredprivate CustomJobFactory customJobFactory;SneakyThrowsBeanpublic Scheduler scheduler(){SchedulerFactory schedulerFactory new StdSchedulerFactory();Scheduler scheduler schedulerFactory.getScheduler();// 自定义 JobFactory 使得在 Quartz Job 中可以使用 Autowiredscheduler.setJobFactory(customJobFactory);scheduler.start();return scheduler;}}总结
本文是基于 Quartz 实现的动态定时任务有些场景比如任务暂停、任务删除、任务立即执行参考下面的源码这里不再赘述了。 项目代码