架构师系列- 定时任务(二)- Quartz框架

quartz特点

Quartz是一个优秀的任务调度框架, 具有以下特点

  • 强大的调度功能,例如支持丰富多样的调度方法,可以满足各种常规及特殊需求;
  • 负载均衡
  • 高可用
quartz 架构体系

Quartz 设计有四个核心类,分别是Scheduler(调度器)、Job(任务) 、Trigger(触发器)、JobDetail(任务详情),他们是使用Quartz的关键。

调度器作为作业的总指挥,触发器作为作业的操作者,作业为应用的功能模块,其关系如下图所示:

Job接口

定时任务的接口,具体定时任务需要实现该接口

定义需要执行的任务,该类是一个接口,只定义了一个方法execute(JobExecutionContext context),在实现类的execute方法中编写所需要定时执行的Job(任务),JobExcutionContext类提供了调度应用的一些信息。Job运行时的信息保存在JobDataMap实例中。

public class MyJob implements Job {@Overridepublic void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {System.out.println("开始执行定时任务...");}
}
Trigger接口

负责设置调度策略,该类是一个接口,描述触发job执行的时间触发规则

有以下这些子类,其中经常用到的是cronTigger

公共属性
  • triggerKey:表示Trigger身份的属性
  • jobKey:Trigger触发时被执行的Job的身份
  • startTime:Trigger第一次触发的时间
  • endTime:Trigger失效的时间点
  • 优先级(priority):如果Trigger很多,或者Quartz线程池的工作线程太少,Quartz可能没有足够的资源同时触发所有的Trigger,这种情况下,如果希望某些Trigger优先被触发,就需要给它设置优先级,Trigger默认的优先级为5,优先级priority属性的值可以是任意整数,正数、负数都可以。(只有同时触发的Trigger之间才会比较优先级)
SimpleTrigger

指定从某一个时间开始,以一定时间间隔(单位:毫秒)执行的任务

关键属性

  • repeatInterval:重复间隔
  • repeatCount:重复次数,实际执行次数是repeatCount+1(因为在startTime的时候一定会执行一次)

代码示例

TriggerBuilder.newTrigger()//设置Trigger的name以及group.withIdentity("my_job_tigger", "my_job_tigger_group")//trigger 开始生效时间.startAt(new Date(System.currentTimeMillis() + 5000))//调度策略.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).withRepeatCount(10))//trigger开始失效时间.endAt(new Date(System.currentTimeMillis() + 15000))//任务名词.forJob("自定义JOB").build();

 

CalendarIntervalTrigger

类似于SimpleTrigger,指定从某一个时间开始,以一定的时间间隔执行的任务

但是不同的是SimpleTrigger指定的时间间隔为毫秒,没办法指定每隔一个月执行一次(每月的时间间隔不是固定值),而CalendarIntervalTrigger支持的间隔单位有秒,分钟,小时,天,月,年,星期

优点

  1. 更方便,比如每隔1小时执行,你不用自己去计算1小时等于多少毫秒
  2. 支持不是固定长度的间隔,比如间隔为月和年。但劣势是精度只能到秒

关键属性

  • interval 执行间隔:intervalUnit 执行间隔的单位(秒,分钟,小时,天,月,年,星期)

代码示例

TriggerBuilder.newTrigger()//设置Trigger的name以及group.withIdentity("my_job_tigger", "my_job_tigger_group")//trigger 开始生效时间,马上生效.startNow()//调度策略.withSchedule(CalendarIntervalScheduleBuilder.calendarIntervalSchedule().withInterval(10, DateBuilder.IntervalUnit.SECOND))//trigger开始失效时间.endAt(new Date(System.currentTimeMillis() + 15000))//任务名词.forJob("calendar_tigger_test").build();
DailyTimeIntervalTrigger

指定每天的某个时间段内,以一定的时间间隔执行任务,并且它可以支持指定星期

关键属性

  • startTimeOfDay:每天开始时间
  • endTimeOfDay:每天结束时间
  • daysOfWeek:需要执行的星期

代码案例

TriggerBuilder.newTrigger()//设置Trigger的name以及group.withIdentity("my_job_tigger", "my_job_tigger_group")//trigger 开始生效时间.startNow()//调度策略.withSchedule(DailyTimeIntervalScheduleBuilder.dailyTimeIntervalSchedule()//早上10点开始执行.startingDailyAt(TimeOfDay.hourAndMinuteOfDay(10, 0))//晚上8点停止执行.endingDailyAt(TimeOfDay.hourAndMinuteOfDay(20, 0))// 周一到周四执行,不写即每天执行.onDaysOfTheWeek(DateBuilder.MONDAY, DateBuilder.TUESDAY, DateBuilder.WEDNESDAY, DateBuilder.THURSDAY)//一小时执行一次.withIntervalInHours(1)//重复执行10次,总共执行11次.withRepeatCount(10))//trigger开始失效时间.endAt(new Date(System.currentTimeMillis() + 15000))//任务名词.forJob("calendar_tigger_test").build();
CronTrigger

适合于更复杂的任务,它支持类型于Linux Cron的语法(并且更强大)

代码案例

TriggerBuilder.newTrigger()//设置Trigger的name以及group.withIdentity("my_job_tigger", "my_job_tigger_group")//trigger 开始生效时间.startNow()//调度策略 每隔5S执行一次.withSchedule(CronScheduleBuilder.cronSchedule("*/5 * * * * ?"))//trigger开始失效时间.endAt(new Date(System.currentTimeMillis() + 15000))//任务名词.forJob("calendar_tigger_test").build();

 

JobDetail

描述Job的实现类及其它相关的静态信息,如:Job名字、描述、关联监听器等信息

Quartz在每次执行Job时,都重新创建一个Job实例,所以它不直接接受一个Job的实例,相反它接收一个Job实现类,以便运行时通过newInstance()的反射机制实例化Job。

因此需要通过一个类来描述Job的实现类及其它相关的静态信息,如Job名字、描述、关联监听器等信息,JobDetail承担了这一角色,JobDetail 用来保存我们作业的详细信息。

一个JobDetail可以有多个Trigger,但是一个Trigger只能对应一个JobDetail

JobBuilder.newJob(MyJob.class).withIdentity("MyJob_1", "JobGroup_1").build();
Scheduler

调度器就相当于一个容器,装载着任务和触发器

Scheduler负责管理Quartz的运行环境,Quartz它是基于多线程架构的,它启动的时候会初始化一套线程,这套线程会用来执行一些预置的作业。

Trigger和JobDetail可以注册到Scheduler中,Scheduler可以将Trigger绑定到某一JobDetail中,这样当Trigger触发时,对应的Job就被执行

Scheduler拥有一个SchedulerContext,它类似于ServletContext,保存着Scheduler上下文信息,Job和Trigger都可以访问SchedulerContext内的信息。Scheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提高运行效率

创建调度器

Scheduler接口有两个实现类,分别为StdScheduler(标准默认调度器)和RemoteScheduler(远程调度器),我们重点介绍下StdScheduler实例,StdScheduler只提供了一个带参构造方法,此构造需要传递QuartzScheduler和SchedulingContext两个实例参数

public StdScheduler(QuartzScheduler sched, SchedulingContext schedCtxt)

然而我们一般不使用构造方法去创建调度器,而是通过调度器工厂来创建,调度器工厂接口SchedulerFactory提供了两种不同类型的工厂实现,分别是DirectSchedulerFactoryStdSchedulerFactory

DirectSchedulerFactory一般用的比较少,更多的场景下我们使用StdSchedulerFactory工厂来创建

创建方式

StdSchedulerFactory提供三种方式创建调度器实例

  1. 通过java.util.Properties属性实例
  2. 通过外部属性文件提供
  3. 通过有属性文件内容的 java.io.InputStream 文件流提供
public static void main(String[] args) {try {StdSchedulerFactory schedulerFactory = new StdSchedulerFactory();// 第一种方式 通过Properties属性实例创建Properties props = new Properties();props.put(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, "org.quartz.simpl.SimpleThreadPool");props.put("org.quartz.threadPool.threadCount", 5);schedulerFactory.initialize(props);// 第二种方式 通过传入文件名// schedulerFactory.initialize("my.properties");// 第三种方式 通过传入包含属性内容的文件输入流// InputStream is = new FileInputStream(new File("my.properties"));// schedulerFactory.initialize(is);// 获取调度器实例Scheduler scheduler = schedulerFactory.getScheduler();} catch (Exception e) {e.printStackTrace();}}

 

集群方案

上面的单机方案存在着单点问题,如果定时任务在多个服务器上运行,则会重复触发,为了解决这些问题,就需要使用quartz的集群方案

集群架构

一个Quartz集群中的每个节点是一个独立的Quartz应用,它又管理着其他的节点。

这就意味着你必须对每个节点分别启动或停止,Quartz集群中,独立的Quartz节点并不与另一节点或是管理节点通信,而是通过相同的数据库表来感知到另一Quartz应用的。

初始化数据库 
docker run -itd --name mysql-quartz -p 3306:3306 -v /opt/scheduleTask/quartz:/opt -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7docker exec -it mysql-quartz bash
mysql> create database quartz default charset 'utf8';
mysql> use quartz;
mysql> source /opt/tables_mysql.sql

 

因为Quartz集群依赖于数据库,所以必须首先创建Quartz数据库表,Quartz发布包中包括了所有被支持的数据库平台的SQL脚本

这些SQL脚本存放于<quartz_home>/docs/dbTables 目录下找到对应数据库的SQL文件这里采用的是tables_mysql.sql

对应表简单含义如下

表明功能
QRTZ_CALENDARS以 Blob 类型存储 Quartz 的 Calendar 信息
QRTZ_CRON_TRIGGERS存储 Cron Trigger,包括 Cron 表达式和时区信息
QRTZ_FIRED_TRIGGERS存储与已触发的 Trigger 相关的状态信息,以及相联 Job 的执行信息
QRTZ_PAUSED_TRIGGER_GRPS存储已暂停的 Trigger 组的信息
QRTZ_SCHEDULER_STATE存储少量的有关 Scheduler 的状态信息,和别的 Scheduler 实例(假如是用于一个集群中)
QRTZ_LOCKS存储程序的悲观锁的信息(假如使用了悲观锁)
QRTZ_JOB_DETAILS存储每一个已配置的 Job 的详细信息
QRTZ_SIMPLE_TRIGGERS存储简单的 Trigger,包括重复次数,间隔,以及已触发的次数
QRTZ_BLOG_TRIGGERSTrigger 作为 Blob 类型存储(用于 Quartz 用户用 JDBC 创建他们自己定制的 Trigger 类型,JobStore 并不知道如何存储实例的时候)
QRTZ_TRIGGER_LISTENERS存储已配置的 TriggerListener 的信息
QRTZ_TRIGGERS存储已配置的 Trigger 的信息
引入pom

将需要的pom文件引入

<dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz</artifactId><version>2.2.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.38</version>
</dependency><!-- https://mvnrepository.com/artifact/commons-lang/commons-lang -->
<dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId><version>2.6</version>
</dependency>
编辑quartz.properties
# Default Properties file for use by StdSchedulerFactory
# to create a Quartz Scheduler Instance, if a different
# properties file is not explicitly specified.
#
#集群配置
org.quartz.scheduler.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
org.quartz.jobStore.misfireThreshold: 60000
#============================================================================
# Configure JobStore
#============================================================================
#默认配置,数据保存到内存
#org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
#持久化配置
org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.useProperties:true
#数据库表前缀
org.quartz.jobStore.tablePrefix:qrtz_
org.quartz.jobStore.dataSource:qzDS#============================================================================
# Configure Datasources
#============================================================================
#JDBC驱动
org.quartz.dataSource.qzDS.driver:com.mysql.jdbc.Driver
org.quartz.dataSource.qzDS.URL:jdbc:mysql://192.168.10.30:3306/quartz
org.quartz.dataSource.qzDS.user:root
org.quartz.dataSource.qzDS.password:123456
org.quartz.dataSource.qzDS.maxConnection:10
整合SpringBoot
注册Quartz注册工厂
该类是将quartz自己创建的类交给spring进行管理以及自动注入@Component
public class QuartzJobFactory extends AdaptableJobFactory {@Autowiredprivate AutowireCapableBeanFactory capableBeanFactory;@Overrideprotected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {//调用父类的方法Object jobInstance = super.createJobInstance(bundle);//进行注入capableBeanFactory.autowireBean(jobInstance);return jobInstance;}
}
注册调度工厂
@Configuration
public class QuartzConfig {@Autowiredprivate QuartzJobFactory jobFactory;@Beanpublic SchedulerFactoryBean schedulerFactoryBean() throws IOException {//获取配置属性PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));//在quartz.properties中的属性被读取并注入后再初始化对象propertiesFactoryBean.afterPropertiesSet();//创建SchedulerFactoryBeanSchedulerFactoryBean factory = new SchedulerFactoryBean();factory.setQuartzProperties(propertiesFactoryBean.getObject());factory.setJobFactory(jobFactory);//支持在JOB实例中注入其他的业务对象factory.setApplicationContextSchedulerContextKey("applicationContextKey");factory.setWaitForJobsToCompleteOnShutdown(true);//这样当spring关闭时,会等待所有已经启动的quartz job结束后spring才能完全shutdown。factory.setOverwriteExistingJobs(false);//是否覆盖己存在的Jobfactory.setStartupDelay(10);//QuartzScheduler 延时启动,应用启动完后 QuartzScheduler 再启动return factory;}/*** 通过SchedulerFactoryBean获取Scheduler的实例** @return* @throws IOException* @throws SchedulerException*/@Bean(name = "scheduler")public Scheduler scheduler() throws IOException, SchedulerException {Scheduler scheduler = schedulerFactoryBean().getScheduler();return scheduler;}
}
配置Quartz数据源
默认 Quartz 的数据连接池是 c3p0,由于性能不太稳定,不推荐使用,因此我们将其改成driud数据连接池public class DruidConnectionProvider implements ConnectionProvider {/*** 常量配置,与quartz.properties文件的key保持一致(去掉前缀),同时提供set方法,Quartz框架自动注入值。** @return* @throws SQLException*///JDBC驱动public String driver;//JDBC连接串public String URL;//数据库用户名public String user;//数据库用户密码public String password;//数据库最大连接数public int maxConnection;//数据库SQL查询每次连接返回执行到连接池,以确保它仍然是有效的。public String validationQuery;private boolean validateOnCheckout;private int idleConnectionValidationSeconds;public String maxCachedStatementsPerConnection;private String discardIdleConnectionsSeconds;public static final int DEFAULT_DB_MAX_CONNECTIONS = 10;public static final int DEFAULT_DB_MAX_CACHED_STATEMENTS_PER_CONNECTION = 120;//Druid连接池private DruidDataSource datasource;@Overridepublic Connection getConnection() throws SQLException {return datasource.getConnection();}@Overridepublic void shutdown() throws SQLException {datasource.close();}@Overridepublic void initialize() throws SQLException {if (this.URL == null) {throw new SQLException("DBPool could not be created: DB URL cannot be null");}if (this.driver == null) {throw new SQLException("DBPool driver could not be created: DB driver class name cannot be null!");}if (this.maxConnection < 0) {throw new SQLException("DBPool maxConnectins could not be created: Max connections must be greater than zero!");}datasource = new DruidDataSource();try {datasource.setDriverClassName(this.driver);} catch (Exception e) {try {throw new SchedulerException("Problem setting driver class name on datasource: " + e.getMessage(), e);} catch (SchedulerException e1) {}}datasource.setUrl(this.URL);datasource.setUsername(this.user);datasource.setPassword(this.password);datasource.setMaxActive(this.maxConnection);datasource.setMinIdle(1);datasource.setMaxWait(0);datasource.setMaxPoolPreparedStatementPerConnectionSize(DEFAULT_DB_MAX_CONNECTIONS);if (this.validationQuery != null) {datasource.setValidationQuery(this.validationQuery);if (!this.validateOnCheckout) {datasource.setTestOnReturn(true);} else {datasource.setTestOnBorrow(true);}datasource.setValidationQueryTimeout(this.idleConnectionValidationSeconds);}}public String getDriver() {return driver;}public void setDriver(String driver) {this.driver = driver;}public String getURL() {return URL;}public void setURL(String URL) {this.URL = URL;}public String getUser() {return user;}public void setUser(String user) {this.user = user;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public int getMaxConnection() {return maxConnection;}public void setMaxConnection(int maxConnection) {this.maxConnection = maxConnection;}public String getValidationQuery() {return validationQuery;}public void setValidationQuery(String validationQuery) {this.validationQuery = validationQuery;}public boolean isValidateOnCheckout() {return validateOnCheckout;}public void setValidateOnCheckout(boolean validateOnCheckout) {this.validateOnCheckout = validateOnCheckout;}public int getIdleConnectionValidationSeconds() {return idleConnectionValidationSeconds;}public void setIdleConnectionValidationSeconds(int idleConnectionValidationSeconds) {this.idleConnectionValidationSeconds = idleConnectionValidationSeconds;}public DruidDataSource getDatasource() {return datasource;}public void setDatasource(DruidDataSource datasource) {this.datasource = datasource;}public String getDiscardIdleConnectionsSeconds() {return discardIdleConnectionsSeconds;}public void setDiscardIdleConnectionsSeconds(String discardIdleConnectionsSeconds) {this.discardIdleConnectionsSeconds = discardIdleConnectionsSeconds;}
}
创建完成之后,还需要在quartz.properties配置文件中设置以下数据源#数据库连接池,将其设置为druid
org.quartz.dataSource.qzDS.connectionProvider.class=cn.itcast.config.DruidConnectionProvider{"jobName":"myJob","groupName":"default","jobClass":"cn.itcast.quartz.MyJob","cronExpression":"0/5 * * * * ?","param":{"hello":"world"}
}
任务管理

默认quartz的功能是有限的,我们可以自己实现quartz的任务管理,比如添加、删除、暂停、运行定时任务

管理接口

该接口是定时任务的管理接口,可以对定时任务进行管理

public interface QuartzJobService {/*** 添加任务可以传参数* @param clazzName* @param jobName* @param groupName* @param cronExp* @param param*/void addJob(String clazzName, String jobName, String groupName, String cronExp, Map<String, Object> param);/*** 暂停任务* @param jobName* @param groupName*/void pauseJob(String jobName, String groupName);/*** 恢复任务* @param jobName* @param groupName*/void resumeJob(String jobName, String groupName);/*** 立即运行一次定时任务* @param jobName* @param groupName*/void runOnce(String jobName, String groupName);/*** 更新任务* @param jobName* @param groupName* @param cronExp* @param param*/void updateJob(String jobName, String groupName, String cronExp, Map<String, Object> param);/*** 删除任务* @param jobName* @param groupName*/void deleteJob(String jobName, String groupName);/*** 启动所有任务*/void startAllJobs();/*** 暂停所有任务*/void pauseAllJobs();/*** 恢复所有任务*/void resumeAllJobs();/*** 关闭所有任务*/void shutdownAllJobs();
}
管理实现类

该类是定时任务的具体实现,是实现了quartz的各种操作

@Service
public class QuartzJobServiceImpl implements QuartzJobService {private static final Logger log = LoggerFactory.getLogger(QuartzJobServiceImpl.class);@Autowiredprivate Scheduler scheduler;@Overridepublic void addJob(String clazzName, String jobName, String groupName, String cronExp, Map<String, Object> param) {try {// 启动调度器,默认初始化的时候已经启动
//            scheduler.start();//构建job信息Class<? extends Job> jobClass = (Class<? extends Job>) Class.forName(clazzName);JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, groupName).build();//表达式调度构建器(即任务执行的时间)CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExp);//按新的cronExpression表达式构建一个新的triggerCronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, groupName).withSchedule(scheduleBuilder).build();//获得JobDataMap,写入数据if (param != null) {trigger.getJobDataMap().putAll(param);}scheduler.scheduleJob(jobDetail, trigger);} catch (Exception e) {log.error("创建任务失败", e);}}@Overridepublic void pauseJob(String jobName, String groupName) {try {scheduler.pauseJob(JobKey.jobKey(jobName, groupName));} catch (SchedulerException e) {log.error("暂停任务失败", e);}}@Overridepublic void resumeJob(String jobName, String groupName) {try {scheduler.resumeJob(JobKey.jobKey(jobName, groupName));} catch (SchedulerException e) {log.error("恢复任务失败", e);}}@Overridepublic void runOnce(String jobName, String groupName) {try {scheduler.triggerJob(JobKey.jobKey(jobName, groupName));} catch (SchedulerException e) {log.error("立即运行一次定时任务失败", e);}}@Overridepublic void updateJob(String jobName, String groupName, String cronExp, Map<String, Object> param) {try {TriggerKey triggerKey = TriggerKey.triggerKey(jobName, groupName);CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);if (cronExp != null) {// 表达式调度构建器CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExp);// 按新的cronExpression表达式重新构建triggertrigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();}//修改mapif (param != null) {trigger.getJobDataMap().putAll(param);}// 按新的trigger重新设置job执行scheduler.rescheduleJob(triggerKey, trigger);} catch (Exception e) {log.error("更新任务失败", e);}}@Overridepublic void deleteJob(String jobName, String groupName) {try {//暂停、移除、删除scheduler.pauseTrigger(TriggerKey.triggerKey(jobName, groupName));scheduler.unscheduleJob(TriggerKey.triggerKey(jobName, groupName));scheduler.deleteJob(JobKey.jobKey(jobName, groupName));} catch (Exception e) {log.error("删除任务失败", e);}}@Overridepublic void startAllJobs() {try {scheduler.start();} catch (Exception e) {log.error("开启所有的任务失败", e);}}@Overridepublic void pauseAllJobs() {try {scheduler.pauseAll();} catch (Exception e) {log.error("暂停所有任务失败", e);}}@Overridepublic void resumeAllJobs() {try {scheduler.resumeAll();} catch (Exception e) {log.error("恢复所有任务失败", e);}}@Overridepublic void shutdownAllJobs() {try {if (!scheduler.isShutdown()) {// 需谨慎操作关闭scheduler容器// scheduler生命周期结束,无法再 start() 启动schedulerscheduler.shutdown(true);}} catch (Exception e) {log.error("关闭所有的任务失败", e);}}
}
API接口
通过实现该接口可以通过外部API对定时任务进行管理@RestController
@RequestMapping("/quartz")
public class QuartzController {private static final Logger log = LoggerFactory.getLogger(QuartzController.class);@Autowiredprivate QuartzJobService quartzJobService;/*** 添加新任务** @param configDTO* @return*/@RequestMapping("/addJob")public Object addJob(@RequestBody QuartzConfigDTO configDTO) {quartzJobService.addJob(configDTO.getJobClass(), configDTO.getJobName(), configDTO.getGroupName(), configDTO.getCronExpression(), configDTO.getParam());return HttpStatus.OK;}/*** 暂停任务** @param configDTO* @return*/@RequestMapping("/pauseJob")public Object pauseJob(@RequestBody QuartzConfigDTO configDTO) {quartzJobService.pauseJob(configDTO.getJobName(), configDTO.getGroupName());return HttpStatus.OK;}/*** 恢复任务** @param configDTO* @return*/@RequestMapping("/resumeJob")public Object resumeJob(@RequestBody QuartzConfigDTO configDTO) {quartzJobService.resumeJob(configDTO.getJobName(), configDTO.getGroupName());return HttpStatus.OK;}/*** 立即运行一次定时任务** @param configDTO* @return*/@RequestMapping("/runOnce")public Object runOnce(@RequestBody QuartzConfigDTO configDTO) {quartzJobService.runOnce(configDTO.getJobName(), configDTO.getGroupName());return HttpStatus.OK;}/*** 更新任务** @param configDTO* @return*/@RequestMapping("/updateJob")public Object updateJob(@RequestBody QuartzConfigDTO configDTO) {quartzJobService.updateJob(configDTO.getJobName(), configDTO.getGroupName(), configDTO.getCronExpression(), configDTO.getParam());return HttpStatus.OK;}/*** 删除任务** @param configDTO* @return*/@RequestMapping("/deleteJob")public Object deleteJob(@RequestBody QuartzConfigDTO configDTO) {quartzJobService.deleteJob(configDTO.getJobName(), configDTO.getGroupName());return HttpStatus.OK;}/*** 启动所有任务** @return*/@RequestMapping("/startAllJobs")public Object startAllJobs() {quartzJobService.startAllJobs();return HttpStatus.OK;}/*** 暂停所有任务** @return*/@RequestMapping("/pauseAllJobs")public Object pauseAllJobs() {quartzJobService.pauseAllJobs();return HttpStatus.OK;}/*** 恢复所有任务** @return*/@RequestMapping("/resumeAllJobs")public Object resumeAllJobs() {quartzJobService.resumeAllJobs();return HttpStatus.OK;}/*** 关闭所有任务** @return*/@RequestMapping("/shutdownAllJobs")public Object shutdownAllJobs() {quartzJobService.shutdownAllJobs();return HttpStatus.OK;}
}
测试

可以通过Postman通过接口动态对定时任务进行管理

添加定时任务

通过PostMan添加任务

添加完成后,可以在控制台看到任务正在执行

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/3636.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一个简单的java递归下降语法分析器例子

import parser.Parser; import parser.RecursiveDescentParser;import java.util.ArrayList; import java.util.Arrays; import java.util.List;public class Main {public static void main(String[] args) {// 关键词List<String> keyList new ArrayList<>(Arra…

NXP i.MX8系列平台开发讲解 - 3.10 Linux PCIe资源分配与访问(二)

目录 1. PCIe BFD 2. PCIe 配置空间 2.1 PCIe 配置空间访问 PCIe I/O访问方法 PCIe MMIO访问方法 3. PCIe BAR相关 4. PCIe Capbility 5. PCIe 操作 本文将重点讲解PCIe的资源访问相关内容&#xff0c;对于PCIe资源访问是从Host 端老看可以对PCIe进行配置与访问的资源主…

【opencv 加速推理】如何安装 支持cuda的opencv 包 用于截帧加速

要在支持CUDA的系统上安装OpenCV&#xff0c;您可以使用pip来安装支持CUDA的OpenCV版本。OpenCV支持CUDA加速&#xff0c;但需要安装额外的库&#xff0c;如cuDNN和NVIDIA CUDA Toolkit。以下是一般步骤&#xff1a; 安装NVIDIA CUDA Toolkit: 首先&#xff0c;您需要安装NVID…

深度学习基础之《TensorFlow框架(12)—图片数据》

一、图像基本知识 1、如何转换图片文件 回忆&#xff1a;之前我们在特征抽取中讲过如何将文本处理成数据 思考&#xff1a;如何将图片文件转换成机器学习算法能够处理的数据&#xff1f; 我们经常接触到的图片有两种&#xff0c;一种是黑白图片&#xff08;灰度图&#xff09;…

网站被SmartScreen标记为不安全怎么办?

在互联网时代&#xff0c;网站的安全性和可信度是用户选择是否继续访问的重要因素之一&#xff0c;然而&#xff0c;网站运营者偶尔会发现使用Edge浏览器访问网站时&#xff0c;会出现Microsoft Defender SmartScreen&#xff08;以下简称SmartScreen&#xff09;提示网站不安全…

Windows下搭建Flutter开发环境

IDE:VS code Flutter官网:Flutter: 为所有屏幕创造精彩 - Flutter 中文开发者网站 - Flutter 下载&安装 下载Flutter SDK,如图,建议自行下载安装: SDK还是挺大的,近1G,使用迅雷下载会快不少。 下载完成,解压缩到指定目录即可! 设置Local SDK,按下面步骤操作即…

【数据结构(邓俊辉)学习笔记】绪论05——动态规划

文章目录 0.前言1. Fibonacci数应用1.1 fib&#xff08;&#xff09;&#xff1a;递归1.1.1 问题与代码1.1.2 复杂度分析1.1.3 递归分析 1.2 fib&#xff08;&#xff09;&#xff1a;迭代 0.前言 make it work,make it right,make it fast. 让代码能够不仅正确而且足够高效地…

适合初学者的自然语言处理 (NLP) 综合指南

一、简述 自然语言处理 (NLP) 是人工智能 (AI) 最热门的领域之一&#xff0c;现在主要指大语言模型了。这要归功于人们热衷于能编写故事的文本生成器、欺骗人们的聊天机器人以及产生照片级真实感的文本到图像程序等应用程序。近年来&#xff0c;计算机理解人类语言、编程语言&a…

前端开发攻略---用原生JS在网页中也能实现文本转语音

1、原理 语音合成 (也被称作是文本转为语音&#xff0c;英语简写是 tts) 包括接收 app 中需要语音合成的文本&#xff0c;再在设备麦克风播放出来这两个过程。 Web API中对此有一个主要控制接口 SpeechSynthesis&#xff0c;外加一些处理如何表示要被合成的文本 (也被称为 utte…

Idea:通义千问插件

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 一、通义千问大模型 二、程序编写助手 三、Idea安装通义千问插件 总结 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、通义千问大模型…

数据结构——二叉树的操作 (层序遍历)(C++实现)

数据结构——二叉树的操作&#xff08;2&#xff09;&#xff08;C实现&#xff09; 统计叶子结点个数统计结点个数层序遍历非递归方式递归方式 我们今天接着来看二叉树的操作&#xff0c;如果还没有看过上一篇的可以点击这里&#xff1a; https://blog.csdn.net/qq_67693066/a…

来自异国的客人 - 华为OD统一考试(D卷)

OD统一考试(D卷) 分值: 100分 题解: Java / Python / C++ 题目描述 有位客人来自异国,在该国使用m进制计数。 该客人有个幸运数字n(n<m),每次购物时,其总是喜欢计算本次支付的花费(折算为异国的价格后)中存在多少幸运数字。 问: 当其购买一个在我国价值k的产品时,…

【Leetcode每日一题】 穷举vs暴搜vs深搜vs回溯vs剪枝_全排列 - 子集(难度⭐⭐)(65)

1. 题目解析 题目链接&#xff1a;78. 子集 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 算法思路详解&#xff1a; 为了生成数组 nums 的所有子集&#xff0c;我们需要对数组中的每个元素进行“选择”或“不选择…

JavaScript-Vue入门

本文主要测分享Vue的一些基础 Vue简介 Vue.js 是一个构建数据驱动的 web 界面的渐进式框架。它的主要目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。 下是一些 Vue 的主要特点和概念&#xff1a; 1. 响应式数据绑定&#xff1a;Vue 使用基于 HTML 的模板语法…

Visual Studio安装MFC开发组件

MFC由于比较古老了&#xff0c;Visual Studio默认没有这个开发组件。最近由于一些原因&#xff0c;需要使用这个库&#xff0c;这就需要另外安装。 参考了网上的一些资料&#xff0c;根据实际使用&#xff0c;其实很多步骤不是必须的。 https://zhuanlan.zhihu.com/p/68117276…

Neo-reGeorg(webshell代理)-php

https://github.com/L-codes/Neo-reGeorg python加载需要的库 pip install requests 生成webshell代理文件 python .\neoreg.py generate -k 123456 其中&#xff0c;123456为密码&#xff0c;可以修改 生成后的文件路径&#xff1a; > neoreg_servers/tunnel.ashx …

Java后台开发的前置说明

1.知识点逻辑 一个部分 都是先挑重点知识点讲解 然后根据这些重点知识点去完成一个项目的开发 然后在到返回来解决这个部分其他细枝末节的知识点 2.软件开发的分工 我们大致可以将软件开发分成四块&#xff1a; 1.前端开发(比如开发电脑中的京东 htmlcssjavascript) 2.移动开…

什么是主数据管理?合理管理主数据,奠定企业数据战略基石

主数据&#xff08;Master Data&#xff09;是指在企业多个业务系统中重复使用、共享的、具有高价值的核心业务实体数据&#xff0c;如客户、产品、供应商、员工等&#xff0c;它们是企业运营和决策的基础&#xff0c;需要保持高度的准确性、一致性和完整性&#xff0c;以确保业…

Scala 05 —— 函数式编程底层逻辑

Scala 05 —— 函数式编程底层逻辑 该文章来自2023/1/14的清华大学交叉信息学院助理教授——袁洋演讲。 文章目录 Scala 05 —— 函数式编程底层逻辑函数式编程假如...副作用是必须的&#xff1f;函数的定义函数是数据的函数&#xff0c;不是数字的函数如何把业务逻辑做成纯函…

贪吃蛇详解

Win32 API介绍&#xff1a; 在写贪吃蛇这款游戏时需要用到一些有关Win32 API的知识&#xff0c; 接下来我会将设计到的知识点列举并讲解&#xff1a; 首先我们先了解一下Win32 API是什么&#xff0c;Windows这个多作业系统除了协调应⽤程序的执⾏、分配内存、管理资源之外&am…