分布式定時(shí)任務(wù)(三)

Quartz分布式定時(shí)任務(wù)的暫停和恢復(fù)等:

前兩篇我們了解了quartz分布式定時(shí)任務(wù)的基本原理和實(shí)現(xiàn)方式,知道所有的定時(shí)任務(wù)都會(huì)被持久化到數(shù)據(jù)庫(kù)求妹。那么我們肯定可以通過(guò)操作數(shù)據(jù)庫(kù)來(lái)做定時(shí)任務(wù)的暫停,恢復(fù)佳窑,立即啟動(dòng)制恍,添加等操作。
事實(shí)上神凑,quartz已經(jīng)給我們提供來(lái)一些列的api接口來(lái)操作對(duì)應(yīng)的定時(shí)任務(wù)净神,我們只需要在這個(gè)基礎(chǔ)之上做進(jìn)一步的擴(kuò)展和封裝就可以實(shí)現(xiàn)我們自己業(yè)務(wù),下面耙厚,將圍繞定時(shí)任務(wù)的控制强挫,提供一個(gè)簡(jiǎn)單的實(shí)現(xiàn)方式。

使用的環(huán)境版本:spring4.x+quartz2.2.x

1,首先薛躬,我們需要?jiǎng)?chuàng)建一個(gè)我們自己job的實(shí)體類ScheduleJob:

/** 
* Created by lyndon on 16/9/13. * job的實(shí)體類
 */
public class ScheduleJob {   

        private String jobNo; //任務(wù)編號(hào)    
        private String jobName; //任務(wù)名稱    
        private String jobGroup; //任務(wù)所屬組    
        private String desc; //任務(wù)描述          
        private String jobStatus; //任務(wù)狀態(tài)    
        private String cronExpression; //任務(wù)對(duì)應(yīng)的時(shí)間表達(dá)式
        private String triggerName; //觸發(fā)器名稱

         //此處省略get和set方法
  }

2, 創(chuàng)建我們自己的QuartzImplService服務(wù)層:



import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

/**
 * Created by lyndon on 16/9/13.
 * quartz_job的工具類
 */
@Service
public class QuartzUtils {

    private final Logger logger = LoggerFactory.getLogger(QuartzUtils.class);

    @Resource
    private Scheduler scheduler;


    /**
     *
     * 獲取計(jì)劃任務(wù)列表
     * @return  List<ScheduleJob>
     */
    public List<ScheduleJob> getPlanJobList() throws SchedulerException{
        List<ScheduleJob> jobList = new ArrayList<>();
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);;
        jobKeys = scheduler.getJobKeys(matcher);
        for (JobKey jobKey : jobKeys) {
            List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
            for (Trigger trigger : triggers) {
                ScheduleJob job = new ScheduleJob();
                job.setJobName(jobKey.getName());
                job.setJobGroup(jobKey.getGroup());
                // 此處是我自己業(yè)務(wù)需要俯渤,給每個(gè)定時(shí)任務(wù)配置類對(duì)應(yīng)的編號(hào)和描述
                String value = PropertiesUtils.getStringCN(jobKey.getName());
                if(null != value && !"".equals(value)){
                    job.setJobNo(value.split("/")[0]);
                    job.setDesc(value.split("/")[1]);
                }else{
                    job.setJobNo("0000");
                    job.setDesc("未監(jiān)控任務(wù)");
                }
                job.setTriggerName("觸發(fā)器:" + trigger.getKey());
                Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                job.setJobStatus(triggerState.name());
                if (trigger instanceof CronTrigger) {
                    CronTrigger cronTrigger = (CronTrigger) trigger;
                    String cronExpression = cronTrigger.getCronExpression();
                    job.setCronExpression(cronExpression);
                }
                jobList.add(job);
            }
        }
      // 對(duì)返回的定時(shí)任務(wù)安裝編號(hào)做排序
        Collections.sort(jobList,new Comparator<ScheduleJob>(){
            public int compare(ScheduleJob arg0, ScheduleJob arg1) {
                return arg0.getJobNo().compareTo(arg1.getJobNo());
            }
        });

        return jobList;
    }


    /**
     * 獲取正在運(yùn)行的任務(wù)列表
     * @return List<ScheduleJob>
     */
    public List<ScheduleJob> getCurrentJobList() throws SchedulerException{
        List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();;
        List<ScheduleJob> jobList = new ArrayList<ScheduleJob>(executingJobs.size());;
        for (JobExecutionContext executingJob : executingJobs) {
            ScheduleJob job = new ScheduleJob();
            JobDetail jobDetail = executingJob.getJobDetail();
            JobKey jobKey = jobDetail.getKey();
            Trigger trigger = executingJob.getTrigger();
            job.setJobName(jobKey.getName());
            job.setJobGroup(jobKey.getGroup());
            String value = PropertiesUtils.getStringCN(jobKey.getName());
            if(null != value && !"".equals(value)){
                job.setJobNo(value.split("/")[0]);
                job.setDesc(value.split("/")[1]);
            }else{
                job.setJobNo("0000");
                job.setDesc("未監(jiān)控任務(wù)");
            }
            job.setTriggerName("觸發(fā)器:" + trigger.getKey());
            Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
            job.setJobStatus(triggerState.name());
            if (trigger instanceof CronTrigger) {
                CronTrigger cronTrigger = (CronTrigger) trigger;
                String cronExpression = cronTrigger.getCronExpression();
                job.setCronExpression(cronExpression);
            }
            jobList.add(job);
        }
        Collections.sort(jobList,new Comparator<ScheduleJob>(){
            public int compare(ScheduleJob arg0, ScheduleJob arg1) {
                return arg0.getJobNo().compareTo(arg1.getJobNo());
            }
        });
        return  jobList;
    }

    /**
     * 暫停當(dāng)前任務(wù)
     * @param scheduleJob
     */
    public void pauseJob(ScheduleJob scheduleJob) throws SchedulerException{
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        if(scheduler.checkExists(jobKey)){
            scheduler.pauseJob(jobKey);
        }
    }

    /**
     * 恢復(fù)當(dāng)前任務(wù)
     * @param scheduleJob
     */
    public void resumeJob(ScheduleJob scheduleJob) throws SchedulerException{
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());

        if(scheduler.checkExists(jobKey)){
            //并恢復(fù)
            scheduler.resumeJob(jobKey);
            //重置當(dāng)前時(shí)間
            this.rescheduleJob(scheduleJob);
        }
    }

    /**
     * 刪除任務(wù)
     * @param scheduleJob
     * @return boolean
     */
    public boolean deleteJob(ScheduleJob scheduleJob) throws SchedulerException{
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        if(scheduler.checkExists(jobKey)){
            return scheduler.deleteJob(jobKey);
        }
        return false;

    }

    /**
     * 立即觸發(fā)當(dāng)前任務(wù)
     * @param scheduleJob
     */
    public void triggerJob(ScheduleJob scheduleJob) throws SchedulerException{
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        if(scheduler.checkExists(jobKey)){
            scheduler.triggerJob(jobKey);
        }

    }

    /**
     * 更新任務(wù)的時(shí)間表達(dá)式
     * @param scheduleJob
     * @return Date
     */
    public Date rescheduleJob(ScheduleJob scheduleJob) throws SchedulerException{
        TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(),
                scheduleJob.getJobGroup());
        if(scheduler.checkExists(triggerKey)){
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob
                    .getCronExpression());
            //按新的cronExpression表達(dá)式重新構(gòu)建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
                    .withSchedule(scheduleBuilder).build();
            //按新的trigger重新設(shè)置job執(zhí)行
            return scheduler.rescheduleJob(triggerKey, trigger);
        }
        return null;
    }

    /**
     * 查詢其中一個(gè)任務(wù)的狀態(tài)
     * @param scheduleJob
     * @return
     * @throws SchedulerException
     */
    public String scheduleJob(ScheduleJob scheduleJob) throws SchedulerException {
        String status = null;
        TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        if (null != trigger) {
            Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
            status = triggerState.name();
        }
        return status;
    }

    /**
     * 校驗(yàn)job是否已經(jīng)加載
     * @param scheduleJob  JOB基本信息參數(shù)
     * @return          是否已經(jīng)加載
     */
    public boolean checkJobExisted(ScheduleJob scheduleJob) throws SchedulerException {
        return scheduler.checkExists(new JobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup()));
    }

  
    private String getStatuDesc(String status){
        if(status.equalsIgnoreCase("NORMAL")){
            return "正常";
        }else if(status.equalsIgnoreCase("PAUSED")){
            return "暫停";
        }else{
            return "異常";
        }
    }
}

3,提供對(duì)應(yīng)的Controller


import com.innmall.hotelmanager.common.Result;
import com.innmall.hotelmanager.service.quartz.QuartzUtils;
import com.innmall.hotelmanager.service.quartz.ScheduleJob;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;

/**
 * Created by lyndon on 16/9/13.
 */
@RestController
@RequestMapping(value = {"/v1/job"})
public class QuartzController {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private QuartzUtils quartzUtils;

    //獲取定時(shí)任務(wù)的列表
    @RequestMapping(value = {"/getJobList"})
    public Result getPlanJobList(String openId){
        //QuartzUtils quartzUtils = new QuartzUtils();
        List<ScheduleJob> list = null;
        try {
            list = quartzUtils.getPlanJobList();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return Result.success(list);
    }

    //暫停任務(wù)
    @RequestMapping(value = {"/pauseJob"})
    public Result pauseJob(String openId){
        //QuartzUtils quartzUtils = new QuartzUtils();
        ScheduleJob job = new ScheduleJob();
        job.setJobGroup("innmall_job");
        job.setJobName("refreshWxToKenJobDetail");
        try {
            quartzUtils.pauseJob(job);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return Result.success("暫停成功");
    }

    //恢復(fù)任務(wù)
    @RequestMapping(value = {"/resumeJob"})
    public Result resumeJob(String openId){
        //QuartzUtils quartzUtils = new QuartzUtils();
        ScheduleJob job = new ScheduleJob();
        job.setJobGroup("innmall_job");
        job.setJobName("refreshWxToKenJobDetail");
        try {
            quartzUtils.resumeJob(job);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return Result.success("恢復(fù)成功");
    }

    //立即觸發(fā)任務(wù)
    @RequestMapping(value = {"/triggerJob"})
    public Result triggerJob(String openId){
        //QuartzUtils quartzUtils = new QuartzUtils();
        ScheduleJob job = new ScheduleJob();
        job.setJobGroup("innmall_job");
        job.setJobName("refreshWxToKenJobDetail");
        try {
            quartzUtils.triggerJob(job);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return Result.success("觸發(fā)成功");
    }

    //刪除任務(wù)
    @RequestMapping(value = {"/deleteJob"})
    public Result deleteJob(String openId){
        //QuartzUtils quartzUtils = new QuartzUtils();
        ScheduleJob job = new ScheduleJob();
        job.setJobGroup("innmall_job");
        job.setJobName("refreshWxToKenJobDetail");
        try {
            quartzUtils.deleteJob(job);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return Result.success("觸發(fā)成功");
    }
  
}

4型宝,接下來(lái)八匠,我們就可以進(jìn)行單元測(cè)試了絮爷。

5,需要注意的地方:

5.1 service層:

@Resource
private Scheduler scheduler;

這里是因?yàn)槲覀冊(cè)趚ml里面已經(jīng)配置對(duì)應(yīng)的工廠bean梨树,所以可以在這里可以直接注入:

<bean id="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"     
 destroy-method="destroy">

5.2 關(guān)于區(qū)分不同業(yè)務(wù)的觸發(fā)器和任務(wù)坑夯,可以配置job和trigger的group屬性,這樣我們便以區(qū)分抡四,如果不設(shè)置柜蜈,quartz將使用default關(guān)鍵字:

   <bean id="refreshWxToKenJobDetail"
          class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
        <property name="jobClass" value="com.innmall.hotelmanager.timer.RefreshWxToKen"/>
        <property name="durability" value="true" />
        <property name="requestsRecovery" value="true" />
        <property name="group" value="innmall_job"/>
    </bean>
    <bean id="refreshWxToKenTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="jobDetail" ref="refreshWxToKenJobDetail"/>
        <!-- 每10s鐘運(yùn)行一次 -->
        <property name="cronExpression" value="0/10 * * * * ?"/>
        <property name="misfireInstruction" value="2"/>
        <property name="group" value="innmall_trigger"/>
    </bean>

5.3 關(guān)于定時(shí)任務(wù)恢復(fù)后,我們?nèi)绻恍枰屩板e(cuò)過(guò)的定時(shí)任務(wù)再執(zhí)行一次指巡,可以設(shè)置misfireInstruction的屬性淑履,其實(shí)就是

CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING

進(jìn)去可以看見(jiàn)對(duì)應(yīng)的值為2.
并且需要在我們恢復(fù)任務(wù)的時(shí)候調(diào)用更新的方法,可以見(jiàn)上文的QuartzUtil中的方法藻雪。

//重置當(dāng)前時(shí)間
this.rescheduleJob(scheduleJob);

5.4 如果需要定時(shí)任務(wù)恢復(fù)后秘噪,需要將之前錯(cuò)過(guò)的執(zhí)行一次,那么只需要在xml里面去除misfireInstruction屬性勉耀,其實(shí)就是使用默認(rèn)配置指煎,并且在恢復(fù)的時(shí)候不調(diào)用更新的方法。

關(guān)于quartz的使用方法便斥,暫時(shí)就介紹到這里至壤,如果有什么地方有問(wèn)題,歡迎指正椭住,后面將持續(xù)研究對(duì)應(yīng)的異常處理機(jī)制崇渗,敬請(qǐng)關(guān)注~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市京郑,隨后出現(xiàn)的幾起案子宅广,更是在濱河造成了極大的恐慌,老刑警劉巖些举,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件跟狱,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡户魏,警方通過(guò)查閱死者的電腦和手機(jī)驶臊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)叼丑,“玉大人关翎,你說(shuō)我怎么就攤上這事○牛” “怎么了纵寝?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)星立。 經(jīng)常有香客問(wèn)我爽茴,道長(zhǎng)葬凳,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任室奏,我火速辦了婚禮火焰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘胧沫。我一直安慰自己昌简,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布琳袄。 她就那樣靜靜地躺著江场,像睡著了一般。 火紅的嫁衣襯著肌膚如雪窖逗。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,155評(píng)論 1 299
  • 那天餐蔬,我揣著相機(jī)與錄音碎紊,去河邊找鬼。 笑死樊诺,一個(gè)胖子當(dāng)著我的面吹牛仗考,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播词爬,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼秃嗜,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了顿膨?” 一聲冷哼從身側(cè)響起锅锨,我...
    開(kāi)封第一講書(shū)人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎恋沃,沒(méi)想到半個(gè)月后必搞,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡囊咏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年恕洲,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片梅割。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡霜第,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出户辞,到底是詐尸還是另有隱情泌类,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布咆课,位于F島的核電站末誓,受9級(jí)特大地震影響扯俱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜喇澡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一迅栅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧晴玖,春花似錦读存、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至秀睛,卻和暖如春尔当,著一層夾襖步出監(jiān)牢的瞬間蹂安,已是汗流浹背椭迎。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留田盈,地道東北人畜号。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像允瞧,于是被迫代替她去往敵國(guó)和親简软。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

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