溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

springboot項(xiàng)目中如何退出定時(shí)任務(wù)

發(fā)布時(shí)間:2020-10-29 18:42:55 來(lái)源:億速云 閱讀:343 作者:Leah 欄目:開(kāi)發(fā)技術(shù)

springboot項(xiàng)目中如何退出定時(shí)任務(wù)?很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來(lái)學(xué)習(xí)下,希望你能有所收獲。

啟動(dòng)類(lèi)

啟動(dòng)類(lèi)上我們獲取到相應(yīng)的上下文,捕捉相應(yīng)命令。在這里插入代碼片

@SpringBootApplication
/**指定mapper對(duì)應(yīng)包的路徑*/
@MapperScan("com.youlanw.kz.dao")
/**開(kāi)啟計(jì)劃任務(wù)*/
@EnableScheduling
/**開(kāi)啟異常重試機(jī)制*/
@EnableRetry
public class YlkzTaskApplication {

  public static ConfigurableApplicationContext context;
  
  public static void main(String[] args) {
    context = SpringApplication.run(YlkzTaskApplication.class, args);
    /**
     * 捕捉命令實(shí)現(xiàn)優(yōu)雅退出
     */
    MySignalHandler.install("TERM");  //捕捉kill命令
    MySignalHandler.install("INT");   //捕捉ctrl+c命令
  }
}

優(yōu)雅退出配置類(lèi)

通過(guò)install方法捕捉到相應(yīng)的命令,

通過(guò)signalAction方法進(jìn)行總開(kāi)發(fā)的控制。

import org.slf4j.LoggerFactory;
import sun.misc.Signal;
import sun.misc.SignalHandler;
/**
 * @description: 定時(shí)任務(wù)控制類(lèi)(實(shí)現(xiàn)優(yōu)雅退出)
 * @method:
 * @author: mamengmeng
 * @date: 10:51 2018/8/13
 */
public class MySignalHandler implements SignalHandler {

  private final static org.slf4j.Logger logger = LoggerFactory.getLogger(MySignalHandler.class);

  private SignalHandler oldHandler;
  /**
   * 定時(shí)任務(wù)總開(kāi)關(guān)-狀態(tài):true:打開(kāi) false:關(guān)閉
   */
  public static boolean base_flag = true;

  @Override
  public void handle(Signal signal) {
    signalAction(signal);
  }

  public static SignalHandler install(String signalName) {
    Signal diagSignal = new Signal(signalName);
    MySignalHandler instance = new MySignalHandler();
    instance.oldHandler = Signal.handle(diagSignal, instance);
    return instance;
  }

  public void signalAction(Signal signal) {
    try {
      //關(guān)閉總開(kāi)關(guān)
      this.base_flag = false;
      logger.info("\n執(zhí)行優(yōu)雅退出操作\n等待運(yùn)行中任務(wù)執(zhí)行完畢…………");
      Thread.sleep(3000);
      StringBuffer stringBuffer = new StringBuffer("a");
      //此處為相關(guān)的業(yè)務(wù)代碼,只要還有一個(gè)定時(shí)任務(wù)在執(zhí)行,那么就等待線(xiàn)程任務(wù)執(zhí)行完畢。
      while (BaseApplyTask.apply_flag || BaseResumeTask.resume_flag || CorpDemandTask.demand_flag || RecommendResumeTask.resume_flag || BaseCodeTask.code_flag || RecommendoneTask.resume_flag ||ResumeByZcbTask.zpbresume_flag) {
        //等待線(xiàn)程任務(wù)執(zhí)行完畢
        stringBuffer.append("");
      }
      //獲取到的上下文對(duì)象關(guān)閉相應(yīng)的程序。
      YlkzTaskApplication.context.close();
      logger.info("\n================\n程序已安全退出!\n================");
      oldHandler.handle(signal);
    } catch (Exception e) {
      logger.error("handle|Signal handler" + "failed, reason "
          + e.getMessage());
      e.printStackTrace();
    }
  }
}

舉例說(shuō)明

我們?cè)诙〞r(shí)任務(wù)中添加一個(gè)總開(kāi)關(guān),當(dāng)總開(kāi)關(guān)是關(guān)著時(shí)是不允許定時(shí)任務(wù)執(zhí)行的,

@Component
public class BaseCodeTask {
  private final static Logger logger = LoggerFactory.getLogger(BaseCodeTask.class);

  @Autowired
  private ResumeService resumeService;

  public static boolean code_flag = true;      //簡(jiǎn)歷任務(wù)執(zhí)行狀態(tài) true:執(zhí)行中 false:執(zhí)行完畢
  private static final Integer LIMIT = 500;
  private final static long time = 60 * 1000;    //一分鐘
  /**
   * @param
   * @description: 同步簡(jiǎn)歷信息(定時(shí)任務(wù))
   * 任務(wù)執(zhí)行間隔時(shí)間:6秒
   * 待同步數(shù)據(jù)為空,則5分鐘后執(zhí)行下一次
   * @method: sendResume
   * @author: zhengmingjie
   * @date: 16:17 2018/8/3
   * @return: void
   */
  @Scheduled(initialDelay = 1000, fixedDelay = time / 10)
  @Async
  public void sendResume() throws Exception {
    List<Resume> list = null;
    try {
      //總開(kāi)關(guān)狀態(tài):true:打開(kāi) false:關(guān)閉
      if (!MySignalHandler.base_flag)
        return;
      this.code_flag = true;
      logger.info("\n======定時(shí)任務(wù):初始化基本數(shù)據(jù)======\n開(kāi)始執(zhí)行\(zhòng)n");
      //以下是業(yè)務(wù)代碼。相關(guān)的定時(shí)任務(wù)批處理
      resumeService.initializationMap();
      resumeService.setCodeDictionary();
      resumeService.setCityInfo();
      resumeService.setCodePostInfo();
      logger.info("\n======定時(shí)任務(wù):初始化基本數(shù)據(jù)======\n結(jié)束\n");
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      this.code_flag = false;
    }
  }
}

定時(shí)任務(wù)優(yōu)雅退出的使用可以有效的防止批處理任務(wù)的中斷,小伙伴們可以嘗試添加哦。。。。

補(bǔ)充知識(shí):springboot自帶定時(shí)器實(shí)現(xiàn)定時(shí)任務(wù)的開(kāi)啟關(guān)閉以及動(dòng)態(tài)修改定時(shí)規(guī)則

最近項(xiàng)目中遇到了需要自動(dòng)定時(shí)導(dǎo)出的需求,用戶(hù)可以從頁(yè)面修改導(dǎo)出的時(shí)間規(guī)則,可以啟用和停用定時(shí)任務(wù)。

經(jīng)過(guò)了解,項(xiàng)目中目前實(shí)現(xiàn)定時(shí)任務(wù),一般有三種選擇,一是用Java自帶的timer類(lèi)。稍微看了一下,可以實(shí)現(xiàn)大部分的指定頻率的任務(wù)的調(diào)度(timer.schedule()),也可以實(shí)現(xiàn)關(guān)閉和開(kāi)啟(timer.cancle)。但是用其來(lái)實(shí)現(xiàn)某天的某個(gè)時(shí)間或者某月的某一天調(diào)度任務(wù)有點(diǎn)不方便。

二是采用Quartz 調(diào)度器實(shí)現(xiàn)。這是一個(gè)功能很強(qiáng)大的開(kāi)源的專(zhuān)門(mén)用于定時(shí)任務(wù)調(diào)度的框架,也很好的和springboot整合,缺點(diǎn):配置復(fù)雜,需要花費(fèi)一定的時(shí)間去了解和研究。(本人懶,因此沒(méi)有選擇這個(gè),但是這個(gè)功能地區(qū)強(qiáng)大,有時(shí)間研究)

三是spring3.0以后自帶的scheduletask任務(wù)調(diào)度,可以實(shí)現(xiàn)quartz的大部分功能,不需要額外引用jar,也不需要另外配置。而且支持注解和配置文件兩種。

因此最后選擇直接用spring自帶的task 實(shí)現(xiàn)。

基本用法很簡(jiǎn)單,通過(guò)在方法上加注解@schedule(也可以通過(guò)xml文件配置的方式),注解里有 cron ,fixedDelay ,fixedRate ,initialDelay 等等參數(shù),可以完成指定時(shí)間,平率執(zhí)行此方法。這里不詳細(xì)介紹。

直接介紹,通過(guò)頁(yè)面動(dòng)態(tài)修改cron參數(shù),修改定時(shí)規(guī)則的思路。

1 實(shí)現(xiàn)接口SchedulingConfigurer,這個(gè)接口只有一個(gè)方法,配置定時(shí)任務(wù)。重寫(xiě)此方法,添加新的任務(wù)實(shí)現(xiàn)runable和新的觸發(fā) 實(shí)現(xiàn)trigger 。

2 在新的觸發(fā)里,把修改的cron寫(xiě)入新的觸發(fā)

3 寫(xiě)UI 方法,接收前端修改的定時(shí)參數(shù)。

代碼如下:

package com.fiberhome.ms.cus.cashform.ui;

import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
@Component
public class DynamicScheduledTask implements SchedulingConfigurer {
@Autowired
private ScheduleExport scheduleExport;

// private static String DEFAULT_CRON = "0/10 * * * * &#63;";
private String cron = "";

public String getCron() {
return cron;
}

public void setCron(String cron) {
this.cron = cron;
}

@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
// TODO Auto-generated method stub
taskRegistrar.addTriggerTask(new Runnable() {

@Override
public void run() {
// TODO Auto-generated method stub
try {
scheduleExport.scheduleTaskExport();//異步定時(shí)生成文件
System.out.println("Msg:定時(shí)生成文件成功");
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
System.out.println("Error:定時(shí)生成文件錯(cuò)誤");
}
}
}, new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
// TODO Auto-generated method stub
if ("".equals(cron)|| cron == null)
return null;
CronTrigger trigger = new CronTrigger(cron);// 定時(shí)任務(wù)觸發(fā),可修改定時(shí)任務(wù)的執(zhí)行周期
Date nextExecDate = trigger.nextExecutionTime(triggerContext);
return nextExecDate;
}
});
System.out.println("can&#63;");
}
}

這個(gè)方法可以實(shí)現(xiàn) 根據(jù)頁(yè)面設(shè)置動(dòng)態(tài)修改定時(shí)器的cron參數(shù),不用重啟服務(wù)。但是運(yùn)行之后發(fā)現(xiàn)了一個(gè)缺陷,即必須在修改完之后,只有再一次到達(dá)定時(shí)任務(wù)的時(shí)間,才會(huì)調(diào)用新的觸發(fā)時(shí)間, 這就導(dǎo)致,頁(yè)面設(shè)置的時(shí)間并不能即時(shí)生效,這在項(xiàng)目中是不符合用戶(hù)的要求,于是為了解決這個(gè)bug,換了另外一種解決方法。

思路:(了解ThreadPoolTaskScheduler這個(gè)類(lèi),TaskScheduler接口的默認(rèn)實(shí)現(xiàn)類(lèi),多線(xiàn)程定時(shí)任務(wù)執(zhí)行??梢栽O(shè)置執(zhí)行線(xiàn)程池?cái)?shù)(默認(rèn)一個(gè)線(xiàn)程))

1、ThreadPoolTaskScheduler 實(shí)現(xiàn)TaskScheduler,可以通過(guò)方法 schedule(java.lang.Runnable task, Trigger trigger),添加定時(shí)任務(wù)和觸發(fā)器。返回java.util.concurrent.ScheduledFuture<&#63;>,future可以控制任務(wù)的開(kāi)關(guān)等。

2、前端修改定時(shí)參數(shù),在set方法中修改ThreadPoolTaskScheduler 的觸發(fā)器。

代碼如下:

package com.fiberhome.ms.cus.cashform.ui.util;

import java.util.Date;
import java.util.concurrent.ScheduledFuture;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import com.fiberhome.ms.cus.cashform.ui.ScheduleExport;

@Component
public class DynamicScheduleTaskSecond {
@Autowired
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
@Autowired
private ScheduleExport scheduleExport;
private ScheduledFuture<&#63;> future;

private String cron = "";

public String getCron() {
return cron;
}

public void setCron(String cron) {
this.cron = cron;
stopCron();
future = threadPoolTaskScheduler.schedule(new Runnable() {

@Override
public void run() {
// TODO Auto-generated method stub
try {
scheduleExport.scheduleTaskExport();// 異步定時(shí)生成文件
System.out.println("Msg:定時(shí)生成文件成功");
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
System.out.println("Error:定時(shí)生成文件錯(cuò)誤");
}
}
}, new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
// TODO Auto-generated method stub
if ("".equals(cron) || cron == null)
return null;
CronTrigger trigger = new CronTrigger(cron);// 定時(shí)任務(wù)觸發(fā),可修改定時(shí)任務(wù)的執(zhí)行周期
Date nextExecDate = trigger.nextExecutionTime(triggerContext);
return nextExecDate;
}
});
}

public void stopCron() {
if (future != null) {
future.cancel(true);//取消任務(wù)調(diào)度
}
}
}

看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝您對(duì)億速云的支持。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI