springboot整合quartz定時任務(wù)

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

image

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)

Scheduler

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所示瓤介。

Quartz 集群

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 目錄下。

Quartz數(shù)據(jù)庫表

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

quartz下載

2武学、解壓進(jìn)入到目錄 docs/daTables 目錄里面有數(shù)據(jù)庫表 sql 腳本選擇自己對應(yīng)的數(shù)據(jù)庫腳本,直接執(zhí)行就可以生成 quartz 相關(guān)的表

image
image

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)建時間
}
image

創(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);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末屁奏,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子错负,更是在濱河造成了極大的恐慌坟瓢,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件犹撒,死亡現(xiàn)場離奇詭異折联,居然都是意外死亡,警方通過查閱死者的電腦和手機识颊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門崭庸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谊囚,你說我怎么就攤上這事怕享。” “怎么了镰踏?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵函筋,是天一觀的道長。 經(jīng)常有香客問我奠伪,道長跌帐,這世上最難降的妖魔是什么首懈? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮谨敛,結(jié)果婚禮上究履,老公的妹妹穿的比我還像新娘。我一直安慰自己脸狸,他們只是感情好最仑,可當(dāng)我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著炊甲,像睡著了一般泥彤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上卿啡,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天吟吝,我揣著相機與錄音,去河邊找鬼颈娜。 笑死剑逃,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的官辽。 我是一名探鬼主播蛹磺,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼野崇!你這毒婦竟也來了称开?” 一聲冷哼從身側(cè)響起亩钟,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤乓梨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后清酥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扶镀,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年焰轻,在試婚紗的時候發(fā)現(xiàn)自己被綠了臭觉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡辱志,死狀恐怖蝠筑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情揩懒,我是刑警寧澤什乙,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站已球,受9級特大地震影響臣镣,放射性物質(zhì)發(fā)生泄漏辅愿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一忆某、第九天 我趴在偏房一處隱蔽的房頂上張望点待。 院中可真熱鬧,春花似錦弃舒、人聲如沸癞埠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽燕差。三九已至,卻和暖如春坝冕,著一層夾襖步出監(jiān)牢的瞬間徒探,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工喂窟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留测暗,地道東北人。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓磨澡,卻偏偏與公主長得像碗啄,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子稳摄,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 夏天的蟬 最喜歡說話 夜里的星空 突然變得緊湊 光怪陸離的霓虹 照亮醉酒人的歡歌 我攪動著飲料 撥亂透明的冰 碰著...
    黑貓兒緊張閱讀 187評論 0 0
  • 1 太陽火辣辣地烤著大地稚字,不遠(yuǎn)處傳來知了的聲音。悶熱的天讓人坐立不安厦酬,夏冰在大樹下乘涼也感到急躁胆描。手機響了。她邊接...
    呆萌的老張看世界687閱讀 2,460評論 55 104
  • 六月份畢業(yè)季回來仗阅,宿舍其他人都出去浪了昌讲,留我一人安靜的睡~樓上掉下來的鋼蹦傳來清脆的聲音,對面的幾聲尖叫把我喚醒减噪,...
    曹阿旋閱讀 257評論 0 0
  • 文/伊春雪接龍客椂坛瘢—懸賞任務(wù)第十一期任務(wù)*************其他***********【懸賞】1103:最討...
    伊春雪閱讀 340評論 6 9
  • 王珊 杭州豪士特商貿(mào)有限公司 【日精進(jìn)打卡第55天】 【知~學(xué)習(xí)】 《六項精進(jìn)》2遍 共127遍 《大學(xué)》2...
    07eba81513de閱讀 234評論 0 0