900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > boot-admin整合Quartz实现动态管理定时任务

boot-admin整合Quartz实现动态管理定时任务

时间:2022-01-04 10:11:14

相关推荐

boot-admin整合Quartz实现动态管理定时任务

淄博烧烤爆红出了圈,当你坐在八大局的烧烤摊,面前是火炉、烤串、小饼和蘸料,音乐响起,啤酒倒满,烧烤灵魂的party即将开场的时候,你系统中的Scheduler(调试器),也自动根据设定的Trigger(触发器),从容优雅的启动了一系列的Job(后台定时任务)。工作一切早有安排,又何须费心劳神呢?因为boot-admin早已将Quartz这块肉串在了烤签上!

项目源码仓库github

项目源码仓库gitee

Quartz是一款Java编写的开源任务调度框架,同时它也是Spring默认的任务调度框架。它的作用其实类似于Timer定时器以及ScheduledExecutorService调度线程池,当然Quartz作为一个独立的任务调度框架表现更为出色,功能更强大,能够定义更为复杂的执行规则。

boot-admin 是一款采用前后端分离模式、基于 SpringCloud 微服务架构 + vue-element-admin 的 SaaS 后台管理框架。

那么boot-admin怎样才能将Quartz串成串呢?一共分三步:

加入依赖

<dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz</artifactId><version>2.3.2</version></dependency>

前端整合

vue页面以el-table作为任务的展示控件,串起任务的创建、修改、删除、挂起、恢复、状态查看等功能。

vue页面

<template><div class="app-container" style="background-color: #FFFFFF;"><!--功能按钮区--><div class="cl pd-5 bg-1 bk-gray"><div align="left" style="float:left"><el-button size="mini" type="primary" @click="search()">查询</el-button><el-button size="mini" type="primary" @click="handleadd()">添加</el-button></div><div align="right"><!--分页控件--><div style="align:right"><el-pagination:current-page="BaseTableData.page.currentPage":page-sizes="[5,10,20,50,100,500]":page-size="BaseTableData.page.pageSize"layout="total, sizes, prev, pager, next, jumper":total="BaseTableData.page.total"@size-change="handlePageSizeChange"@current-change="handlePageCurrentChange"/></div><!--分页控件--></div></div><!--功能按钮区--><!--表格--><el-table max-height="100%" :data="BaseTableData.table" style="width: 100%" :border="true"><el-table-column type="index" :index="indexMethod" /><el-table-column prop="jobName" label="任务名称" width="100px" /><el-table-column prop="jobGroup" label="任务所在组" width="100px" /><el-table-column prop="jobClassName" label="任务类名" /><el-table-column prop="cronExpression" label="表达式" width="120" /><el-table-column prop="timeZoneId" label="时区" width="120" /><el-table-column prop="startTime" label="开始" width="120" :formatter="(row,column,cellValue) => dateTimeColFormatter(row,column,cellValue)"/><el-table-column prop="nextFireTime" label="下次" width="120" :formatter="(row,column,cellValue) => dateTimeColFormatter(row,column,cellValue)"/><el-table-column prop="previousFireTime" label="上次" width="120" :formatter="(row,column,cellValue) => dateTimeColFormatter(row,column,cellValue)"/><el-table-column prop="triggerState" label="状态" width="80"><template slot-scope="scope"><p v-if="scope.row.triggerState=='NORMAL'">等待</p><p v-if="scope.row.triggerState=='PAUSED'">暂停</p><p v-if="scope.row.triggerState=='NONE'">删除</p><p v-if="scope.row.triggerState=='COMPLETE'">结束</p><p v-if="scope.row.triggerState=='ERROR'">错误</p><p v-if="scope.row.triggerState=='BLOCKED'">阻塞</p></template></el-table-column><el-table-column label="操作" width="220px"><template slot-scope="scope"><el-button type="warning" size="least" title="挂起" @click="handlePause(scope.row)">挂起</el-button><el-button type="primary" size="least" title="恢复" @click="handleResume(scope.row)">恢复</el-button><el-button type="danger" size="least" title="删除" @click="handleDelete(scope.row)">删除</el-button><el-button type="success" size="least" title="修改" @click="handleUpdate(scope.row)">修改</el-button></template></el-table-column></el-table><!--表格--><!--主表单弹出窗口--><el-dialogv-cloaktitle="维护":visible.sync="InputBaseInfoDialogData.dialogVisible":close-on-click-modal="InputBaseInfoDialogData.showCloseButton"top="5vh":show-close="InputBaseInfoDialogData.showCloseButton":fullscreen="InputBaseInfoDialogData.dialogFullScreen"><!--弹窗头部header--><div slot="title" style="margin-bottom: 10px"><div align="left" style="float:left"><h3>定时任务管理</h3></div><div align="right"><el-button type="text" title="全屏显示" @click="resizeInputBaseInfoDialogMax()"><i class="el-icon-arrow-up" /></el-button><el-button type="text" title="以弹出窗口形式显示" @click="resizeInputBaseInfoDialogNormal()"><i class="el-icon-arrow-down" /></el-button><el-button type="text" title="关闭" @click="closeInputBaseInfoDialog()"><i class="el-icon-error" /></el-button></div></div><!--弹窗头部header--><!--弹窗表单--><el-formref="InputBaseInfoForm":status-icon="InputBaseInfoDialogData.statusIcon":model="InputBaseInfoDialogData.data"class="demo-ruleForm"><el-form-item label="原任务名称" :label-width="InputBaseInfoDialogData.formLabelWidth" prop="jobName">{{ InputBaseInfoDialogData.data.oldJobName }}【修改任务时使用】</el-form-item><el-form-item label="原任务分组" :label-width="InputBaseInfoDialogData.formLabelWidth" prop="jobGroup">{{ InputBaseInfoDialogData.data.oldJobGroup }}【修改任务时使用】</el-form-item><el-form-item label="任务名称" :label-width="InputBaseInfoDialogData.formLabelWidth" prop="jobName"><el-input v-model="InputBaseInfoDialogData.data.jobName" auto-complete="off" /></el-form-item><el-form-item label="任务分组" :label-width="InputBaseInfoDialogData.formLabelWidth" prop="jobGroup"><el-input v-model="InputBaseInfoDialogData.data.jobGroup" auto-complete="off" /></el-form-item><el-form-item label="类名" :label-width="InputBaseInfoDialogData.formLabelWidth" prop="jobClassName"><el-input v-model="InputBaseInfoDialogData.data.jobClassName" auto-complete="off" /></el-form-item><el-form-item label="表达式" :label-width="InputBaseInfoDialogData.formLabelWidth" prop="cronExpression"><el-input v-model="InputBaseInfoDialogData.data.cronExpression" auto-complete="off" /></el-form-item></el-form><!--弹窗表单--><!--弹窗尾部footer--><div slot="footer" class="dialog-footer"><el-button type="primary" @click="saveInputBaseInfoForm()">保 存</el-button></div><!--弹窗尾部footer--></el-dialog><!--弹出窗口--><!--查看场所弹出窗口--><el-dialogv-cloaktitle="修改任务":visible.sync="ViewBaseInfoDialogData.dialogVisible":close-on-click-modal="ViewBaseInfoDialogData.showCloseButton"top="5vh":show-close="ViewBaseInfoDialogData.showCloseButton":fullscreen="ViewBaseInfoDialogData.dialogFullScreen"><!--弹窗头部header--><div slot="title" style="margin-bottom: 10px"><div align="left" style="float:left"><h3>修改任务</h3></div><div align="right"><el-button type="text" @click="dialogResize('ViewBaseInfoDialog',true)"><i class="el-icon-arrow-up" title="全屏显示" /></el-button><el-button type="text" @click="dialogResize('ViewBaseInfoDialog',false)"><iclass="el-icon-arrow-down"title="以弹出窗口形式显示"/></el-button><el-button type="text" @click="dialogClose('ViewBaseInfoDialog')"><i class="el-icon-error" title="关闭" /></el-button></div></div><!--弹窗头部header--><!--弹窗表单--><el-formref="ViewBaseInfoForm":status-icon="ViewBaseInfoDialogData.statusIcon":model="ViewBaseInfoDialogData.data"class="demo-ruleForm"><el-form-item label="表达式" :label-width="ViewBaseInfoDialogData.formLabelWidth" prop="cronExpression">{{ this.BaseTableData.currentRow.cronExpression }}</el-form-item></el-form><!--弹窗表单--></el-dialog></div></template><script>import {getBlankJob,fetchJobPage,getUpdateObject,saveJob,pauseJob,resumeJob,deleteJob} from '@/api/job'export default {name: 'Jobmanage',data: function() {return {/*** 后台服务忙,防止重复提交的控制变量* */ServiceRunning: false,/***表格和分页组件* */BaseTableData: {currentRow: {},page: {currentPage: 1,pageSize: 20,pageNum: 1,pages: 1,size: 5,total: 1},/***主表格数据* */table: [],/***勾选选中的数据* */selected: []},InputBaseInfoDialogData: {data: {},dialogVisible: false,dialogFullScreen: false,formLabelWidth: '180px',showCloseButton: false,statusIcon: true},ViewBaseInfoDialogData: {cronExpression: '',dialogVisible: false,dialogFullScreen: true,formLabelWidth: '180px'}}},/***初始化自动执行查询表格数据--不用调整**/mounted: function() {this.loadTableData()},methods: {/*** 查询---------根据实际调整参数*/async loadTableData() {if (this.ServiceRunning) {this.$message({message: '请不要重复点击。',type: 'warning'})return}this.ServiceRunning = trueconst response = await fetchJobPage(this.BaseTableData.page)if (response.code !== 100) {this.ServiceRunning = falsethis.$message({message: response.message,type: 'warning'})return}const {data} = responsethis.BaseTableData.page.total = data.totalthis.BaseTableData.table = data.recordsthis.ServiceRunning = false},/*** 每页大小调整事件* @param val*/handlePageSizeChange(val) {if (val != this.BaseTableData.page.pageSize) {this.BaseTableData.page.pageSize = valthis.loadTableData()}},/*** 当前面号调整事件* @param val*/handlePageCurrentChange(val) {if (val != this.BaseTableData.page.currentPage) {this.BaseTableData.page.currentPage = valthis.loadTableData()}},dialogResize(dialogName, toMax) {VFC_dialogResize(dialogName, toMax)},resizeInputBaseInfoDialogMax() {this.InputBaseInfoDialogData.dialogFullScreen = true},resizeInputBaseInfoDialogNormal() {this.InputBaseInfoDialogData.dialogFullScreen = false},dialogClose(dialogName) {},closeInputBaseInfoDialog() {this.InputBaseInfoDialogData.dialogVisible = falsethis.loadTableData()},async getBlankForm() {const response = await getBlankJob()if (response.code !== 100) {this.ServiceRunning = falsethis.$message({message: response.message,type: 'warning'})return}const {data} = responsethis.InputBaseInfoDialogData.data = data},async getUpdateForm(row) {const response = await getUpdateObject(row)if (response.code !== 100) {this.ServiceRunning = falsethis.$message({message: response.message,type: 'warning'})return}const {data} = responsethis.InputBaseInfoDialogData.data = data},// 弹出对话框handleadd() {this.getBlankForm()this.InputBaseInfoDialogData.dialogVisible = true},handleUpdate(row) {if (row.triggerState !== 'PAUSED') {this.$message({message: '请先挂起任务,再修改。',type: 'warning'})return}this.getUpdateForm(row)this.InputBaseInfoDialogData.dialogVisible = true},search() {this.loadTableData()},/*** 提交修改主表单*/async saveInputBaseInfoForm() {if (this.ServiceRunning) {this.$message({message: '请不要重复点击。',type: 'warning'})return}this.ServiceRunning = trueconst response = await saveJob(this.InputBaseInfoDialogData.data)if (response.code !== 100) {this.ServiceRunning = falsethis.$message({message: response.message,type: 'warning'})return}this.ServiceRunning = falsethis.$message({message: '数据保存成功。',type: 'success'})this.loadTableData()},async handlePause(row) {if (this.ServiceRunning) {this.$message({message: '请不要重复点击。',type: 'warning'})return}this.ServiceRunning = trueconst response = await pauseJob(row)if (response.code !== 100) {this.ServiceRunning = falsethis.$message({message: response.message,type: 'warning'})return}this.ServiceRunning = falsethis.$message({message: '任务成功挂起。',type: 'success'})this.loadTableData()},async handleResume(row) {if (this.ServiceRunning) {this.$message({message: '请不要重复点击。',type: 'warning'})return}this.ServiceRunning = trueconst response = await resumeJob(row)if (response.code !== 100) {this.ServiceRunning = falsethis.$message({message: response.message,type: 'warning'})return}this.ServiceRunning = falsethis.$message({message: '任务成功恢复。',type: 'success'})this.loadTableData()},async handleDelete(row) {if (row.triggerState !== 'PAUSED') {this.$message({message: '请先挂起任务,再删除。',type: 'warning'})return}if (this.ServiceRunning) {this.$message({message: '请不要重复点击。',type: 'warning'})return}this.ServiceRunning = trueconst response = await deleteJob(row)if (response.code !== 100) {this.ServiceRunning = falsethis.$message({message: response.message,type: 'warning'})return}this.ServiceRunning = falsethis.$message({message: '任务成功删除。',type: 'success'})this.loadTableData()},indexMethod(index) {return this.BaseTableData.page.pageSize * (this.BaseTableData.page.currentPage - 1) + index + 1},dateTimeColFormatter(row, column, cellValue) {return this.$commonUtils.dateTimeFormat(cellValue)},}}</script><style></style>

api定义

job.js定义访问后台接口的方式

import request from '@/utils/request'//获取空任务export function getBlankJob() {return request({url: '/api/system/auth/job/blank',method: 'get'})}//获取任务列表(分页)export function fetchJobPage(data) {return request({url: '/api/system/auth/job/page',method: 'post',data})}//获取用于修改的任务信息export function getUpdateObject(data) {return request({url: '/api/system/auth/job/dataforupdate',method: 'post',data})}//保存任务export function saveJob(data) {return request({url: '/api/system/auth/job/save',method: 'post',data})}//暂停任务export function pauseJob(data) {return request({url: '/api/system/auth/job/pause',method: 'post',data})}//恢复任务export function resumeJob(data) {return request({url: '/api/system/auth/job/resume',method: 'post',data})}//删除任务export function deleteJob(data) {return request({url: '/api/system/auth/job/delete',method: 'post',data})}

后端整合

配置类

单独数据源配置

Quartz会自动创建11张数据表,数据源可以与系统主数据源相同,也可以独立设置。

笔者建议单独设置Quartz数据源。在配置文件 application.yml 添加以下内容

base2048:job:enable: truedatasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/base2048job?useSSL=false&serverTimezone=UTC&autoReconnect=true&allowPublicKeyRetrieval=true&useOldAliasMetadataBehavior=trueusername: rootpassword: mysql

数据源配置类如下:

@Configurationpublic class QuartzDataSourceConfig {@Primary@Bean(name = "defaultDataSource")@ConfigurationProperties(prefix = "spring.datasource")public DruidDataSource druidDataSource() {return new DruidDataSource();}@Bean(name = "quartzDataSource")@QuartzDataSource@ConfigurationProperties(prefix = "base2048.job.datasource")public DruidDataSource quartzDataSource() {return new DruidDataSource();}}

调度器配置

在 resources 下添加 quartz.properties 文件,内容如下:

# 固定前缀org.quartz# 主要分为scheduler、threadPool、jobStore、plugin等部分##org.quartz.scheduler.instanceName = DefaultQuartzSchedulerorg.quartz.scheduler.rmi.export = falseorg.quartz.scheduler.rmi.proxy = falseorg.quartz.scheduler.wrapJobExecutionInUserTransaction = false<!-- 每个集群节点要有独立的instanceId -->org.quartz.scheduler.instanceId = 'AUTO'# 实例化ThreadPool时,使用的线程类为SimpleThreadPoolorg.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool# threadCount和threadPriority将以setter的形式注入ThreadPool实例# 并发个数org.quartz.threadPool.threadCount = 15# 优先级org.quartz.threadPool.threadPriority = 5org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = trueorg.quartz.jobStore.misfireThreshold = 5000# 默认存储在内存中#org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore#持久化org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTXorg.quartz.jobStore.tablePrefix = QRTZ_org.quartz.jobStore.dataSource = qzDSorg.quartz.dataSource.qzDS.maxConnections = 10

调度器配置类内容如下:

@Configurationpublic class SchedulerConfig {@Autowiredprivate MyJobFactory myJobFactory;@Value("${base2048.job.enable:false}")private Boolean JOB_LOCAL_RUNING;@Value("${base2048.job.datasource.driver-class-name}")private String dsDriver;@Value("${base2048.job.datasource.url}")private String dsUrl;@Value("${base2048.job.datasource.username}")private String dsUser;@Value("${base2048.job.datasource.password}")private String dsPassword;@Beanpublic SchedulerFactoryBean schedulerFactoryBean() throws IOException {SchedulerFactoryBean factory = new SchedulerFactoryBean();factory.setOverwriteExistingJobs(true);// 延时启动factory.setStartupDelay(20);// 用于quartz集群,QuartzScheduler 启动时更新己存在的Job// factory.setOverwriteExistingJobs(true);// 加载quartz数据源配置factory.setQuartzProperties(quartzProperties());// 自定义Job Factory,用于Spring注入factory.setJobFactory(myJobFactory);// 在com.neusoft.jn.gpbase.quartz.job.BaseJobTemplate 同样出现该配置//原因 : qrtz 在集群模式下 存在 同一个任务 一个在A服务器任务被分配出去 另一个B服务器任务不再分配的情况.//if(!JOB_LOCAL_RUNING){// 设置调度器自动运行factory.setAutoStartup(false);}return factory;}@Beanpublic Properties quartzProperties() throws IOException {PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));propertiesFactoryBean.afterPropertiesSet();Properties properties = propertiesFactoryBean.getObject();properties.setProperty("org.quartz.dataSource.qzDS.driver",dsDriver);properties.setProperty("org.quartz.dataSource.qzDS.URL",dsUrl);properties.setProperty("org.quartz.dataSource.qzDS.user",dsUser);properties.setProperty("org.quartz.dataSource.qzDS.password",dsPassword);return properties;}/** 通过SchedulerFactoryBean获取Scheduler的实例*/@Bean(name="scheduler")public Scheduler scheduler() throws Exception {return schedulerFactoryBean().getScheduler();}}

任务模板

Job基类

public abstract class BaseJob implements Job, Serializable {private static final String JOB_MAP_KEY = "self";public static final String STATUS_RUNNING = "1";public static final String STATUS_NOT_RUNNING = "0";public static final String CONCURRENT_IS = "1";public static final String CONCURRENT_NOT = "0";/*** 任务名称*/private String jobName;/*** 任务分组*/private String jobGroup;/*** 任务状态 是否启动任务*/private String jobStatus;/*** cron表达式*/private String cronExpression;/*** 描述*/private String description;/*** 任务执行时调用哪个类的方法 包名+类名*/private Class beanClass = this.getClass();/*** 任务是否有状态*/private String isConcurrent;/*** Spring bean*/private String springBean;/*** 任务调用的方法名*/private String methodName;/*** 为了将执行后的任务持久化到数据库中*/@JsonIgnoreprivate JobDataMap dataMap = new JobDataMap();public JobKey getJobKey(){return JobKey.jobKey(jobName, jobGroup);// 任务名称和组构成任务key}public JobDataMap getDataMap(){if(dataMap.size() == 0){dataMap.put(JOB_MAP_KEY,this);}return dataMap;}public String getJobName() {return jobName;}public void setJobName(String jobName) {this.jobName = jobName;}public String getJobGroup() {return jobGroup;}public void setJobGroup(String jobGroup) {this.jobGroup = jobGroup;}public String getJobStatus() {return jobStatus;}public void setJobStatus(String jobStatus) {this.jobStatus = jobStatus;}public String getCronExpression() {return cronExpression;}public void setCronExpression(String cronExpression) {this.cronExpression = cronExpression;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}public Class getBeanClass() {return beanClass;}public void setBeanClass(Class beanClass) {this.beanClass = beanClass;}public String getIsConcurrent() {return isConcurrent;}public void setIsConcurrent(String isConcurrent) {this.isConcurrent = isConcurrent;}public String getSpringBean() {return springBean;}public void setSpringBean(String springBean) {this.springBean = springBean;}public String getMethodName() {return methodName;}public void setMethodName(String methodName) {this.methodName = methodName;}}

Job模板类

@Slf4jpublic abstract class BaseJobTemplate extends BaseJob {@Value("${base2048.job.enable:false}")private Boolean JOB_LOCAL_RUNING;@Overridepublic final void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {if (JOB_LOCAL_RUNING) {try {this.runing(jobExecutionContext);} catch (Exception ex) {throw new JobExecutionException(ex);}} else {log.info("配置参数不允许在本机执行定时任务");}}public abstract void runing(JobExecutionContext jobExecutionContext);}

Job示例类

业务Job从模板类继承。

@Slf4j@Component@DisallowConcurrentExecutionpublic class TestJob extends BaseJobTemplate {@Overridepublic void runing(JobExecutionContext jobExecutionContext) {try {log.info("测试任务开始:【{}】", Instant.now().atOffset(ZoneOffset.ofHours(8)));System.out.println("============= 测试任务正在运行 =====================");System.out.println("============= Test job is running ===============");log.info("测试任务结束:【{}】", Instant.now().atOffset(ZoneOffset.ofHours(8)));} catch (Exception ex) {log.error("测试任务异常:【{}】", Instant.now().atOffset(ZoneOffset.ofHours(8)));log.error(ex.getMessage(), ex);}}}

管理功能

Controller

@RestController@RequestMapping("/api/system/auth/job")@Slf4jpublic class QuartzJobController {@Resourceprivate QuartzService quartzService;@PostMapping("/save")@ApiOperation(value = "保存添加或修改任务",notes = "保存添加或修改任务")public ResultDTO addOrUpdate(@RequestBody JobUpdateDTO jobUpdateDTO) throws Exception {if (StringUtils.isBlank(jobUpdateDTO.getOldJobName())) {ResultDTO resultDTO = this.addSave(jobUpdateDTO);return resultDTO;} else {/*** 先删除后添加*/JobDTO jobDTO = new JobDTO();jobDTO.setJobName(jobUpdateDTO.getOldJobName());jobDTO.setJobGroup(jobUpdateDTO.getOldJobGroup());this.delete(jobDTO);ResultDTO resultDTO = this.addSave(jobUpdateDTO);return resultDTO;}}private ResultDTO addSave(@RequestBody JobUpdateDTO jobUpdateDTO) throws Exception {BaseJob job = (BaseJob) Class.forName(jobUpdateDTO.getJobClassName()).newInstance();job.setJobName(jobUpdateDTO.getJobName());job.setJobGroup(jobUpdateDTO.getJobGroup());job.setDescription(jobUpdateDTO.getDescription());job.setCronExpression(jobUpdateDTO.getCronExpression());try {quartzService.addJob(job);return ResultDTO.success();}catch (Exception ex){log.error(ex.getMessage(),ex);return ResultDTO.failureCustom("保存添加任务时服务发生意外情况。");}}@PostMapping("/page")@ApiOperation(value = "查询任务",notes = "查询任务")public ResultDTO getJobPage(@RequestBody BasePageQueryVO basePageQueryVO) {try {IPage<JobDTO> jobDtoPage = quartzService.queryJob(basePageQueryVO.getCurrentPage(),basePageQueryVO.getPageSize());return ResultDTO.success(jobDtoPage);}catch (Exception ex){log.error(ex.getMessage(),ex);return ResultDTO.failureCustom("查询任务时服务发生意外情况。");}}@PostMapping("/pause")@ApiOperation(value = "暂停任务",notes = "暂停任务")public ResultDTO pause(@RequestBody JobDTO jobDTO) {try {quartzService.pauseJob(jobDTO.getJobName(),jobDTO.getJobGroup());return ResultDTO.success();}catch (Exception ex){log.error(ex.getMessage(),ex);return ResultDTO.failureCustom("暂停任务时服务发生意外情况。");}}@PostMapping("/resume")@ApiOperation(value = "恢复任务",notes = "恢复任务")public ResultDTO resume(@RequestBody JobDTO jobDTO) {try {quartzService.resumeJob(jobDTO.getJobName(),jobDTO.getJobGroup());return ResultDTO.success();}catch (Exception ex){log.error(ex.getMessage(),ex);return ResultDTO.failureCustom("恢复任务时服务发生意外情况。");}}@PostMapping("/delete")@ApiOperation(value = "删除任务",notes = "删除任务")public ResultDTO delete(@RequestBody JobDTO jobDTO) {try {if(quartzService.deleteJob(jobDTO.getJobName(),jobDTO.getJobGroup())) {return ResultDTO.failureCustom("删除失败。");}else{return ResultDTO.success();}}catch (Exception ex){log.error(ex.getMessage(),ex);return ResultDTO.failureCustom("删除任务时服务发生意外情况。");}}@GetMapping("/blank")public ResultDTO getBlankJobDTO(){JobUpdateDTO jobUpdateDTO = new JobUpdateDTO();jobUpdateDTO.setJobClassName("com.qiyuan.base2048.quartz.job.jobs.");jobUpdateDTO.setCronExpression("*/9 * * * * ?");return ResultDTO.success(jobUpdateDTO);}@PostMapping("/dataforupdate")public ResultDTO getUpdateJobDTO(@RequestBody JobDTO jobDTO){JobUpdateDTO jobUpdateDTO = JobDtoTransMapper.INSTANCE.map(jobDTO);jobUpdateDTO.setOldJobName(jobDTO.getJobName());jobUpdateDTO.setOldJobGroup(jobDTO.getJobGroup());return ResultDTO.success(jobUpdateDTO);}}

JobDTO

@Datapublic class JobDTO {private String jobClassName;private String jobName;private String jobGroup;private String description;private String cronExpression;private String triggerName;private String triggerGroup;private String timeZoneId;private String triggerState;private Date startTime;private Date nextFireTime;private Date previousFireTime;}

JobUpdateDTO

@Datapublic class JobUpdateDTO extends JobDTO{private String oldJobName;private String oldJobGroup;}

Service

@Service@Slf4jpublic class QuartzServiceImpl implements QuartzService {/*** Scheduler代表一个调度容器,一个调度容器可以注册多个JobDetail和Trigger.当Trigger和JobDetail组合,就可以被Scheduler容器调度了*/@Autowiredprivate Scheduler scheduler;@Resourceprivate QrtzJobDetailsMapper qrtzJobDetailsMapper;@Autowiredprivate SchedulerFactoryBean schedulerFactoryBean;@Autowiredpublic QuartzServiceImpl(Scheduler scheduler){this.scheduler = scheduler;}@Overridepublic IPage<JobDTO> queryJob(int pageNum, int pageSize) throws Exception{List<JobDTO> jobList = null;try {Scheduler scheduler = schedulerFactoryBean.getScheduler();GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);jobList = new ArrayList<>();for (JobKey jobKey : jobKeys) {List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);for (Trigger trigger : triggers) {JobDTO jobDetails = new JobDTO();if (trigger instanceof CronTrigger) {CronTrigger cronTrigger = (CronTrigger) trigger;jobDetails.setCronExpression(cronTrigger.getCronExpression());jobDetails.setTimeZoneId(cronTrigger.getTimeZone().getDisplayName());}jobDetails.setTriggerGroup(trigger.getKey().getName());jobDetails.setTriggerName(trigger.getKey().getGroup());jobDetails.setJobGroup(jobKey.getGroup());jobDetails.setJobName(jobKey.getName());jobDetails.setStartTime(trigger.getStartTime());jobDetails.setJobClassName(scheduler.getJobDetail(jobKey).getJobClass().getName());jobDetails.setNextFireTime(trigger.getNextFireTime());jobDetails.setPreviousFireTime(trigger.getPreviousFireTime());jobDetails.setTriggerState(scheduler.getTriggerState(trigger.getKey()).name());jobList.add(jobDetails);}}} catch (SchedulerException e) {e.printStackTrace();}IPage<JobDTO> jobDTOPage = new Page<>(pageNum,pageSize);jobDTOPage.setRecords(jobList);jobDTOPage.setTotal(jobList.size());jobDTOPage.setCurrent(1);jobDTOPage.setPages(1);jobDTOPage.setSize(jobList.size());return jobDTOPage;}/*** 添加一个任务* @param job* @throws SchedulerException*/@Overridepublic void addJob(BaseJob job) throws SchedulerException {/** 创建JobDetail实例,绑定Job实现类* JobDetail 表示一个具体的可执行的调度程序,job是这个可执行调度程序所要执行的内容* 另外JobDetail还包含了这个任务调度的方案和策略**/// 指明job的名称,所在组的名称,以及绑定job类JobDetail jobDetail = JobBuilder.newJob(job.getBeanClass()).withIdentity(job.getJobKey()).withDescription(job.getDescription()).usingJobData(job.getDataMap()).build();/*** Trigger代表一个调度参数的配置,什么时候去调度*///定义调度触发规则, 使用cronTrigger规则Trigger trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(),job.getJobGroup()).withSchedule(CronScheduleBuilder.cronSchedule(job.getCronExpression())).startNow().build();//将任务和触发器注册到任务调度中去scheduler.scheduleJob(jobDetail,trigger);//判断调度器是否启动if(!scheduler.isStarted()){scheduler.start();}log.info(String.format("定时任务:%s.%s-已添加到调度器!", job.getJobGroup(),job.getJobName()));}/*** 根据任务名和任务组名来暂停一个任务* @param jobName* @param jobGroupName* @throws SchedulerException*/@Overridepublic void pauseJob(String jobName,String jobGroupName) throws SchedulerException {scheduler.pauseJob(JobKey.jobKey(jobName,jobGroupName));}/*** 根据任务名和任务组名来恢复一个任务* @param jobName* @param jobGroupName* @throws SchedulerException*/@Overridepublic void resumeJob(String jobName,String jobGroupName) throws SchedulerException {scheduler.resumeJob(JobKey.jobKey(jobName,jobGroupName));}public void rescheduleJob(String jobName,String jobGroupName,String cronExpression,String description) throws SchedulerException {TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);// 表达式调度构建器CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);// 按新的cronExpression表达式重新构建triggertrigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withDescription(description).withSchedule(scheduleBuilder).build();// 按新的trigger重新设置job执行scheduler.rescheduleJob(triggerKey, trigger);}/*** 根据任务名和任务组名来删除一个任务* @param jobName* @param jobGroupName* @throws SchedulerException*/@Overridepublic boolean deleteJob(String jobName,String jobGroupName) throws SchedulerException {TriggerKey triggerKey = TriggerKey.triggerKey(jobName,jobGroupName);scheduler.pauseTrigger(triggerKey); //先暂停scheduler.unscheduleJob(triggerKey); //取消调度boolean flag = scheduler.deleteJob(JobKey.jobKey(jobName,jobGroupName)); return flag;}private JobDTO createJob(String jobName, String jobGroup, Scheduler scheduler, Trigger trigger)throws SchedulerException {JobDTO job = new JobDTO();job.setJobName(jobName);job.setJobGroup(jobGroup);job.setDescription("触发器:" + trigger.getKey());Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());job.setTriggerState(triggerState.name());if(trigger instanceof CronTrigger) {CronTrigger cronTrigger = (CronTrigger)trigger;String cronExpression = cronTrigger.getCronExpression();job.setCronExpression(cronExpression);}return job;}}

至此,烤串完毕,火侯正好,外酥里嫩!

项目源码仓库github

项目源码仓库gitee

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。