1. quartz 的基本實現(xiàn)原理
Quartz 任務(wù)調(diào)度的核心元素為:
Scheduler 任務(wù)調(diào)度器
Trigger 觸發(fā)器
Job 任務(wù)
其中 trigger 和 job 是任務(wù)調(diào)度的元數(shù)據(jù)绍撞,scheduler 是實際執(zhí)行調(diào)度的控制器傻铣。
1.1 Trigger 觸發(fā)器
Trigger 是用于定義調(diào)度時間的元素非洲,即按照什么時間規(guī)則去執(zhí)行任務(wù)。Quartz 中主要提供了四種類型的 trigger:
SimpleTrigger
CronTirgger
DateIntervalTrigger
NthIncludedDayTrigger
這四種 trigger 可以滿足企業(yè)應(yīng)用中的絕大部分需求。
1.2 Job 任務(wù)
Job 用于表示被調(diào)度的任務(wù)朴皆。主要有兩種類型的 job:
stateless(無狀態(tài)的)
stateful(有狀態(tài)的)
對于同一個 trigger 來說遂铡,有狀態(tài)的 job 不能被并行執(zhí)行扒接,只有上一次觸發(fā)的任務(wù)被執(zhí)行完之后珠增,才能觸發(fā)下一次執(zhí)行蒂教。Job 主要有兩種屬性:volatility 和 durability凝垛,其中 volatility 表示任務(wù)是否被持久化到數(shù)據(jù)庫存儲梦皮,而 durability 表示在沒有 trigger 關(guān)聯(lián)的時候任務(wù)是否被保留。兩者都是在值為 true 的時候任務(wù)被持久化或保留让网。一個 job 可以被多個 trigger 關(guān)聯(lián)溃睹,但是一個 trigger 只能關(guān)聯(lián)一個 job因篇。
1.3 Scheduler 任務(wù)調(diào)度器
Scheduler 由 scheduler 工廠創(chuàng)建:DirectSchedulerFactory
或者 StdSchedulerFactory
竞滓。
第二種工廠 StdSchedulerFactory
使用較多商佑,因為 DirectSchedulerFactory
使用起來不夠方便莉御,需要作許多詳細(xì)的手工編碼設(shè)置礁叔。
Scheduler 主要有三種:
RemoteMBeanScheduler
RemoteScheduler
StdScheduler
2. Quartz 線程
在 Quartz 中,有兩類線程涣易,Scheduler 調(diào)度線程和任務(wù)執(zhí)行線程新症,其中任務(wù)執(zhí)行線程通常使用一個線程池維護(hù)一組線程。
2.1 Scheduler 調(diào)度線程主要有兩個:
1隆嗅、執(zhí)行常規(guī)調(diào)度的線程
常規(guī)調(diào)度線程輪詢存儲的所有 trigger胖喳,如果有需要觸發(fā)的 trigger丽焊,即到達(dá)了下一次觸發(fā)的時間,則從任務(wù)執(zhí)行線程池獲取一個空閑線程凫乖,執(zhí)行與該 trigger 關(guān)聯(lián)的任務(wù)帽芽。
2导街、執(zhí)行 misfired trigger 的線程
Misfire 線程是掃描所有的 trigger款票,查看是否有 misfiredtrigger艾少,如果有的話根據(jù) misfire 的策略分別處理(fire now OR wait for the next fire)
2.2 Quartz Job 數(shù)據(jù)存儲
Quartz 中的 trigger 和 job 需要存儲下來才能被使用。
Quartz 中有兩種存儲方式:
RAMJobStore :將 trigger 和 job 存儲在內(nèi)存中
JobStoreSupport :基于 jdbc 將 trigger 和 job 存儲到數(shù)據(jù)庫中
RAMJobStore 的存取速度非车危快雏吭,但是由于其在系統(tǒng)被停止后所有的數(shù)據(jù)都會丟失,所以在集群應(yīng)用中胀莹,必須使用JobStoreSupport描焰。
2.3 Quartz 集群架構(gòu)
一個 Quartz 集群中的每個節(jié)點是一個獨立的 Quartz 應(yīng)用荆秦,它又管理著其他的節(jié)點。這就意味著你必須對每個節(jié)點分別啟動或停止力图。Quartz 集群中步绸,獨立的 Quartz 節(jié)點并不與另一其的節(jié)點或是管理節(jié)點通信,而是通過相同的數(shù)據(jù)庫表來感知到另一 Quartz 應(yīng)用的吃媒,如圖2.1所示瓤介。
2.4 Quartz 集群相關(guān)數(shù)據(jù)庫表
因為 Quartz 集群依賴于數(shù)據(jù)庫吕喘,所以必須首先創(chuàng)建 Quartz 數(shù)據(jù)庫表刑桑,Quartz 發(fā)布包中包括了所有被支持的數(shù)據(jù)庫平臺的 SQL 腳本氯质。這些 SQL 腳本存放于 <quartz_home>/docs/dbTables
目錄下。
QRTZ_JOB_DETAILS: 存儲的是 job 的詳細(xì)信息祠斧,包括:[DESCRIPTION
]描述闻察,[IS_DURABLE
]是否持久化,[JOB_DATA
]持久化對象等基本信息琢锋。
QRTZ_TRIGGERS: 觸發(fā)器信息辕漂,包含:job 的名,組外鍵吴超,[DESCRIPTION
]觸發(fā)器的描述等基本信息钉嘹,還有[START_TIME
]開始執(zhí)行時間,[END_TIME
]結(jié)束執(zhí)行時間烛芬,[PREV_FIRE_TIME
]上次執(zhí)行時間,[NEXT_FIRE_TIME
]下次執(zhí)行時間飒责,[TRIGGER_TYPE
]觸發(fā)器類型:simple 和 cron赘娄,[TRIGGER_STATE
]執(zhí)行狀態(tài):WAITING,PAUSED宏蛉,ACQUIRED 分別為:等待遣臼、暫停、運行中拾并。
QRTZ_CRON_TRIGGERS: 保存 cron 表達(dá)式揍堰。
QRTZ_SCHEDULER_STATE: 存儲集群中 note 實例信息,quartz 會定時讀取該表的信息判斷集群中每個實例的當(dāng)前狀態(tài)嗅义,INSTANCE_NAME:之前配置文件中 org.quartz.scheduler.instanceId
配置的名字屏歹,就會寫入該字段,如果設(shè)置為 AUTO之碗,quartz 會根據(jù)物理機名和當(dāng)前時間產(chǎn)生一個名字蝙眶。 [LAST_CHECKIN_TIME
]上次檢查時間,[CHECKIN_INTERVAL
]檢查間隔時間褪那。
QRTZ_PAUSED_TRIGGER_GRPS: 暫停的任務(wù)組信息幽纷。
QRTZ_LOCKS: 悲觀鎖發(fā)生的記錄信息。
QRTZ_FIRED_TRIGGERS: 正在運行的觸發(fā)器信息博敬。
QRTZ_SIMPLE_TRIGGERS: 簡單的觸發(fā)器詳細(xì)信息友浸。
QRTZ_BLOB_TRIGGERS: 觸發(fā)器存為二進(jìn)制大對象類型(用于 Quartz 用戶自己觸發(fā)數(shù)據(jù)庫定制自己的觸發(fā)器,然而 JobStore 不明白怎么存放實例的時候)偏窝。
QRTZ_CALENDARS: 以 Blob 類型存儲 Quartz 的 Calendar 信息
數(shù)據(jù)庫相關(guān)表介紹:http://www.cnblogs.com/zhenyuyaodidiao/p/4755649.html
參數(shù)配置介紹:http://blog.csdn.net/zixiao217/article/details/53091812
3. SpringBoot 整合 Quartz
創(chuàng)建 quartz 的相關(guān)的數(shù)據(jù)庫中的表
1收恢、在官網(wǎng)下載 quartz
2武学、解壓進(jìn)入到目錄 docs/daTables
目錄里面有數(shù)據(jù)庫表 sql 腳本選擇自己對應(yīng)的數(shù)據(jù)庫腳本,直接執(zhí)行就可以生成 quartz 相關(guān)的表
pom.xml引入依賴
<!--集成quartz-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>${quartz.version}</version>
<!-- quartz默認(rèn)使用c3p0連接池派诬,如果項目使用的不是則需要排除依賴包 -->
<exclusions>
<exclusion>
<artifactId>c3p0</artifactId>
<groupId>c3p0</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
創(chuàng)建配置文件類
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import javax.sql.DataSource;
import java.util.Properties;
/**
* 定時任務(wù)配置
*
*/
@Configuration
public class QuartzConfig {
@Bean
public SchedulerFactoryBean scheduler(@Qualifier("druidDataSource") DataSource dataSource) {
//quartz參數(shù)
Properties prop = new Properties();
//配置實例
//prop.put("org.quartz.scheduler.instanceName", "MyScheduler");//實例名稱
prop.put("org.quartz.scheduler.instanceId", "AUTO");
//線程池配置
prop.put("org.quartz.threadPool.threadCount", "5");
//JobStore配置
prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setDataSource(dataSource);
factory.setQuartzProperties(prop);
factory.setSchedulerName("MyScheduler");//數(shù)據(jù)庫中存儲的名字
//QuartzScheduler 延時啟動劳淆,應(yīng)用啟動5秒后 QuartzScheduler 再啟動
factory.setStartupDelay(5);
//factory.setApplicationContextSchedulerContextKey("applicationContextKey");
//可選,QuartzScheduler 啟動時更新己存在的Job默赂,這樣就不用每次修改targetObject后刪除qrtz_job_details表對應(yīng)記錄了
factory.setOverwriteExistingJobs(true);
//設(shè)置自動啟動沛鸵,默認(rèn)為true
factory.setAutoStartup(true);
return factory;
}
}
創(chuàng)建自定義 Job 任務(wù)類,繼承 QuartzJobBean
import com.alibaba.fastjson.JSON;
import com.qfedu.rongzaiboot.entity.ScheduleJob;
import com.qfedu.rongzaiboot.entity.ScheduleJobLog;
import com.qfedu.rongzaiboot.service.ScheduleJobLogService;
import com.qfedu.rongzaiboot.utils.SpringContextUtils;
import org.apache.commons.lang.StringUtils;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Method;
import java.util.Date;
public class QuartzJob extends QuartzJobBean {
private Logger logger = LoggerFactory.getLogger(getClass());
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
System.out.println("執(zhí)行quartz任務(wù)缆八。曲掰。。奈辰。栏妖。");
String json = context.getMergedJobDataMap().getString("JOB_PARAM_KEY");
//將獲取的對象序列化的json 轉(zhuǎn)化為實體類對象
ScheduleJob scheduleJob = JSON.parseObject(json, ScheduleJob.class);
Long jobId = scheduleJob.getJobId();
String beanName = scheduleJob.getBeanName();
String methodName = scheduleJob.getMethodName();
String params = scheduleJob.getParams();
//quartz沒有被spring管理 所以通過其它方式獲取service
ScheduleJobLogService scheduleJobLogService = (ScheduleJobLogService) SpringContextUtils.getBean("scheduleJobLogServiceImpl");
//保存任務(wù)記錄日志
ScheduleJobLog scheduleJobLog = new ScheduleJobLog();
scheduleJobLog.setJobId(jobId);
scheduleJobLog.setBeanName(beanName);
scheduleJobLog.setMethodName(methodName);
scheduleJobLog.setParams(params);
scheduleJobLog.setCreateTime(new Date());
long startTime = System.currentTimeMillis();
try {
Object targetClass = SpringContextUtils.getBean(beanName);
Method method = null;
//通過反射獲取方法
if (StringUtils.isNotBlank(params)) {
method = targetClass.getClass().getDeclaredMethod(methodName, String.class);
} else {
method = targetClass.getClass().getDeclaredMethod(methodName);
}
ReflectionUtils.makeAccessible(method);//使方法具有public權(quán)限
//根據(jù)反射執(zhí)行方法
if (StringUtils.isNotBlank(params)) {
method.invoke(targetClass, params);
} else {
method.invoke(targetClass);
}
long endTime = System.currentTimeMillis() - startTime;
scheduleJobLog.setStatus((byte) 0);//保存日志里的操作狀態(tài) 0:成功
scheduleJobLog.setTimes(endTime);//耗時多長時間
logger.info("任務(wù)執(zhí)行成功,任務(wù)ID:" + jobId + "奖恰,總耗時:" + endTime + "毫秒");
} catch (Exception e) {
long endTime = System.currentTimeMillis() - startTime;
scheduleJobLog.setError(StringUtils.substring(e.toString(),2000));//錯誤消息
scheduleJobLog.setStatus((byte)1);//失敗
scheduleJobLog.setTimes(endTime);//耗時
e.printStackTrace();
logger.error("任務(wù)執(zhí)行失敗吊趾,任務(wù)ID:"+jobId);
} finally {
//最后調(diào)用service保存定時任務(wù)日志記錄
scheduleJobLogService.save(scheduleJobLog);
}
}
}
創(chuàng)建Scheduler工具類,quartz的操作核心瑟啃,包括操作quartz在數(shù)據(jù)庫中的表
import com.alibaba.fastjson.JSON;
import com.qfedu.rongzaiboot.entity.ScheduleJob;
import com.qfedu.rongzaiboot.quartz.QuartzJob;
import org.quartz.*;
public class SchedulerUtils {
/**
* 創(chuàng)建任務(wù)
*/
public static void createJob(Scheduler scheduler, ScheduleJob scheduleJob) {
try {
Long jobId = scheduleJob.getJobId();
//創(chuàng)建Job對象
JobDetail job = JobBuilder.newJob(QuartzJob.class).withIdentity("JOB_" + jobId).build();
//獲取cron表達(dá)式 并創(chuàng)建對象
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
.withMisfireHandlingInstructionDoNothing();
//創(chuàng)建觸發(fā)器
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("TRIGGET_" + jobId)
.withSchedule(cronScheduleBuilder) //將cron表達(dá)式配置到觸發(fā)器
.build();
//將對象josn序列化存儲到Job的getJobDataMap()方法中论泛,為后續(xù)根據(jù)獲取屬性執(zhí)行對應(yīng)的類的任務(wù)
job.getJobDataMap().put("JOB_PARAM_KEY", JSON.toJSONString(scheduleJob));
//存數(shù)據(jù)
scheduler.scheduleJob(job, trigger);
scheduler.pauseJob(JobKey.jobKey("JOB_" + jobId));//使任務(wù)處于等待狀態(tài),創(chuàng)建后不會執(zhí)行
} catch (SchedulerException e) {
throw new RRException("創(chuàng)建任務(wù)失敗", e);
}
}
/**
* 更新任務(wù)
*/
public static void updateJob(Scheduler scheduler, ScheduleJob scheduleJob) {
//獲取新的cron表達(dá)式
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
.withMisfireHandlingInstructionDoNothing();
Long jobId = scheduleJob.getJobId();
try {
//拿到原有的trigger
TriggerKey triggerKey = TriggerKey.triggerKey("TRIGGER_" + jobId);
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
//為原有的trigger賦予新的cron表達(dá)式
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
.withSchedule(cronScheduleBuilder).build();
//執(zhí)行原有的trigger更新
scheduler.rescheduleJob(triggerKey, trigger);
} catch (SchedulerException e) {
e.printStackTrace();
throw new RRException("更新定時任務(wù)失敗", e);
}
}
/**
* 刪除任務(wù)
*/
public static void deleteJob(Scheduler scheduler, Long jobId) {
try {
scheduler.deleteJob(JobKey.jobKey("JOB_" + jobId));
} catch (SchedulerException e) {
e.printStackTrace();
throw new RRException("刪除定時任務(wù)失敗", e);
}
}
/**
* 恢復(fù)任務(wù)
*/
public static void resumeJob(Scheduler scheduler, Long jobId) {
try {
scheduler.resumeJob(JobKey.jobKey("JOB_" + jobId));
} catch (SchedulerException e) {
e.printStackTrace();
throw new RRException("恢復(fù)定時任務(wù)失敗", e);
}
}
/**
* 立即執(zhí)行定時任務(wù)
*/
public static void run(Scheduler scheduler, Long jobId) {
try {
//只執(zhí)行一次并且不會改變?nèi)蝿?wù)的狀態(tài)
scheduler.triggerJob(JobKey.jobKey("JOB_" + jobId));
} catch (SchedulerException e) {
e.printStackTrace();
throw new RRException("立即執(zhí)行定時任務(wù)失敗", e);
}
}
/**
* 暫停任務(wù)
*
* @param scheduler
* @param jobId
*/
public static void pauseJob(Scheduler scheduler, Long jobId) {
try {
scheduler.pauseJob(JobKey.jobKey("JOB_" + jobId));
} catch (SchedulerException e) {
e.printStackTrace();
throw new RRException("暫停定時任務(wù)失敗", e);
}
}
}
保存任務(wù)的自定義類實體類
public class ScheduleJob implements Serializable {
private static final long serialVersionUID = 1L;
private Long jobId;
private String beanName; //執(zhí)行的類名
private String methodName; //方法名
private String params; //參數(shù)
private String cronExpression; //cron表達(dá)式
private Byte status; //任務(wù)狀態(tài) 0,運行 1蛹屿,暫停
private String remark; //備注
private Date createTime; //創(chuàng)建時間
}
創(chuàng)建quartz任務(wù)
controller
/**
* 保存定時任務(wù)
*/
@MyLog("保存定時任務(wù)")
@RequestMapping("/save")
@RequiresPermissions("schedule:job:save")
public R save(@RequestBody ScheduleJob scheduleJob){
scheduleJobService.save(scheduleJob);
return R.ok();
}
service,impl
/**
* 接口:保存定時任務(wù)
*/
void save(ScheduleJob scheduleJob);
//------------------
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void save(ScheduleJob scheduleJob) {
//保存實體類的信息
scheduleJob.setCreateTime(new Date());
scheduleJob.setStatus(Constant.ScheduleStatus.PAUSE.getValue());
scheduleJobMapper.insertSelective(scheduleJob);
//創(chuàng)建定時任務(wù) 并保存到對應(yīng)的quatrz表中
SchedulerUtils.createJob(scheduler, scheduleJob);
}
修改任務(wù)
controller
/**
* 定時任務(wù)信息
*/
@GetMapping("/info/{jobId}")
@RequiresPermissions(value={"schedule:job:info"})
public R info(@PathVariable("jobId") Long jobId){
ScheduleJob scheduleJob = scheduleJobService.queryObject(jobId);
return R.ok().put("scheduleJob", scheduleJob);
}
//-------------------------------------------
@MyLog("修改定時任務(wù)")
@PostMapping("/update")
@RequiresPermissions(value={"schedule:job:update"})
public R update(@RequestBody ScheduleJob scheduleJob){
scheduleJobService.update(scheduleJob);
return R.ok();
}
service,impl
/**
* 查詢
*/
ScheduleJob queryObject(Long jobId);
/**
* 更新定時任務(wù)
*/
void update(ScheduleJob scheduleJob);
//--------------------------------------------------------
@Override
public ScheduleJob queryObject(Long menuId) {
return scheduleJobMapper.selectByPrimaryKey(menuId);
}
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void update(ScheduleJob scheduleJob) {
SchedulerUtils.updateJob(scheduler, scheduleJob);
scheduleJobMapper.updateByPrimaryKeySelective(scheduleJob);
}
dao,mapper
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
select
<include refid="Base_Column_List" />
from schedule_job
where job_id = #{jobId,jdbcType=BIGINT}
</select>
<update id="updateByPrimaryKeySelective" parameterType="com.qfedu.rongzaiboot.entity.ScheduleJob" >
update schedule_job
<set >
<if test="beanName != null" >
bean_name = #{beanName,jdbcType=VARCHAR},
</if>
<if test="methodName != null" >
method_name = #{methodName,jdbcType=VARCHAR},
</if>
<if test="params != null" >
params = #{params,jdbcType=VARCHAR},
</if>
<if test="cronExpression != null" >
cron_expression = #{cronExpression,jdbcType=VARCHAR},
</if>
<if test="status != null" >
status = #{status,jdbcType=TINYINT},
</if>
<if test="remark != null" >
remark = #{remark,jdbcType=VARCHAR},
</if>
<if test="createTime != null" >
create_time = #{createTime,jdbcType=TIMESTAMP},
</if>
</set>
where job_id = #{jobId,jdbcType=BIGINT}
</update>
刪除任務(wù)
controller
/**
* 刪除定時任務(wù)
*/
@MyLog("刪除定時任務(wù)")
@PostMapping("/del")
@RequiresPermissions("schedule:job:delete")
public R delete(@RequestBody Long[] jobIds){
scheduleJobService.deleteBatch(jobIds);
return R.ok();
}
service,impl
/**
* 批量刪除
*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void deleteBatch(Long[] jobIds) {
for(Long jobId : jobIds){
SchedulerUtils.deleteJob(scheduler, jobId);
}
//刪除數(shù)據(jù)
scheduleJobMapper.deleteBatch(jobIds);
}
dao,mapper
<!-- 批量刪除任務(wù)記錄 -->
<delete id="deleteBatch">
delete from schedule_job where job_id in
<foreach collection="array" item="jobId" open="(" separator="," close=")">
#{jobId}
</foreach>
</delete>
暫停定時任務(wù)
controller
/**
* 暫停定時任務(wù)
*/
@MyLog("暫停定時任務(wù)")
@PostMapping("/pause")
@RequiresPermissions("schedule:job:pause")
public R pause(@RequestBody Long[] jobIds){
scheduleJobService.pause(jobIds);
return R.ok();
}
service,impl
/**
* 批量暫停任務(wù)
*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void pause(Long[] jobIds) {
for(Long jobId : jobIds){
SchedulerUtils.pauseJob(scheduler, jobId);
}
Map<String, Object> map = new HashMap<>();
map.put("list", jobIds);
map.put("status", Constant.ScheduleStatus.PAUSE.getValue());
scheduleJobMapper.updateBatch(map);
}
dao,mapper
<!-- 批量更新狀態(tài) -->
<update id="updateBatch">
update schedule_job set status = #{status} where job_id in
<foreach collection="list" item="jobId" open="(" separator="," close=")">
#{jobId}
</foreach>
</update>
恢復(fù)任務(wù)
controller
/**
* 恢復(fù)定時任務(wù)
*/
@MyLog("恢復(fù)定時任務(wù)")
@PostMapping("/resume")
@RequiresPermissions("schedule:job:resume")
public R resume(@RequestBody Long[] jobIds){
scheduleJobService.resume(jobIds);
return R.ok();
}
service,impl
/**
* 恢復(fù)定時任務(wù)
*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void resume(Long[] jobIds) {
for(Long jobId : jobIds){
SchedulerUtils.resumeJob(scheduler, jobId);
}
Map<String, Object> map = new HashMap<>();
map.put("list", jobIds);
map.put("status", Constant.ScheduleStatus.NORMAL.getValue());
scheduleJobMapper.updateBatch(map);
}
dao,mapper
<!-- 批量更新狀態(tài) -->
<update id="updateBatch">
update schedule_job set status = #{status} where job_id in
<foreach collection="list" item="jobId" open="(" separator="," close=")">
#{jobId}
</foreach>
</update>
立即執(zhí)行任務(wù)(項目里被設(shè)置只會執(zhí)行一次)
controller
/**
* 立即執(zhí)行定時任務(wù)
*/
@MyLog("立即執(zhí)行定時任務(wù)")
@PostMapping("/run")
@RequiresPermissions("schedule:job:run")
public R run(@RequestBody Long[] jobIds){
scheduleJobService.run(jobIds);
return R.ok();
}
service,impl
/**
* 立即執(zhí)行定時任務(wù)
*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void run(Long[] jobIds) {
for(Long jobId : jobIds){
SchedulerUtils.run(scheduler, jobId);
}
}