一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 淺談springboot項目中定時任務如何優雅退出

淺談springboot項目中定時任務如何優雅退出

2020-09-30 15:01格調100 Java教程

這篇文章主要介紹了淺談springboot項目中定時任務如何優雅退出?具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧

在一個springboot項目中需要跑定時任務處理批數據時,突然有個Kill命令或者一個Ctrl+C的命令,此時我們需要當批數據處理完畢后才允許定時任務關閉,也就是當定時任務結束時才允許Kill命令生效。

啟動類

啟動類上我們獲取到相應的上下文,捕捉相應命令。在這里插入代碼片

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@SpringBootApplication
/**指定mapper對應包的路徑*/
@MapperScan("com.youlanw.kz.dao")
/**開啟計劃任務*/
@EnableScheduling
/**開啟異常重試機制*/
@EnableRetry
public class YlkzTaskApplication {
 
  public static ConfigurableApplicationContext context;
  
  public static void main(String[] args) {
    context = SpringApplication.run(YlkzTaskApplication.class, args);
    /**
     * 捕捉命令實現優雅退出
     */
    MySignalHandler.install("TERM");  //捕捉kill命令
    MySignalHandler.install("INT");   //捕捉ctrl+c命令
  }
}

優雅退出配置類

通過install方法捕捉到相應的命令,

通過signalAction方法進行總開發的控制。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import org.slf4j.LoggerFactory;
import sun.misc.Signal;
import sun.misc.SignalHandler;
/**
 * @description: 定時任務控制類(實現優雅退出)
 * @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;
  /**
   * 定時任務總開關-狀態:true:打開 false:關閉
   */
  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 {
      //關閉總開關
      this.base_flag = false;
      logger.info("\n執行優雅退出操作\n等待運行中任務執行完畢…………");
      Thread.sleep(3000);
      StringBuffer stringBuffer = new StringBuffer("a");
      //此處為相關的業務代碼,只要還有一個定時任務在執行,那么就等待線程任務執行完畢。
      while (BaseApplyTask.apply_flag || BaseResumeTask.resume_flag || CorpDemandTask.demand_flag || RecommendResumeTask.resume_flag || BaseCodeTask.code_flag || RecommendoneTask.resume_flag ||ResumeByZcbTask.zpbresume_flag) {
        //等待線程任務執行完畢
        stringBuffer.append("");
      }
      //獲取到的上下文對象關閉相應的程序。
      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();
    }
  }
}

舉例說明

我們在定時任務中添加一個總開關,當總開關是關著時是不允許定時任務執行的,

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@Component
public class BaseCodeTask {
  private final static Logger logger = LoggerFactory.getLogger(BaseCodeTask.class);
 
  @Autowired
  private ResumeService resumeService;
 
  public static boolean code_flag = true;      //簡歷任務執行狀態 true:執行中 false:執行完畢
  private static final Integer LIMIT = 500;
  private final static long time = 60 * 1000;    //一分鐘
  /**
   * @param
   * @description: 同步簡歷信息(定時任務)
   * 任務執行間隔時間:6秒
   * 待同步數據為空,則5分鐘后執行下一次
   * @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 {
      //總開關狀態:true:打開 false:關閉
      if (!MySignalHandler.base_flag)
        return;
      this.code_flag = true;
      logger.info("\n======定時任務:初始化基本數據======\n開始執行\n");
      //以下是業務代碼。相關的定時任務批處理
      resumeService.initializationMap();
      resumeService.setCodeDictionary();
      resumeService.setCityInfo();
      resumeService.setCodePostInfo();
      logger.info("\n======定時任務:初始化基本數據======\n結束\n");
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      this.code_flag = false;
    }
  }
}

定時任務優雅退出的使用可以有效的防止批處理任務的中斷,小伙伴們可以嘗試添加哦。。。。

補充知識:springboot自帶定時器實現定時任務的開啟關閉以及動態修改定時規則

最近項目中遇到了需要自動定時導出的需求,用戶可以從頁面修改導出的時間規則,可以啟用和停用定時任務。

經過了解,項目中目前實現定時任務,一般有三種選擇,一是用Java自帶的timer類。稍微看了一下,可以實現大部分的指定頻率的任務的調度(timer.schedule()),也可以實現關閉和開啟(timer.cancle)。但是用其來實現某天的某個時間或者某月的某一天調度任務有點不方便。

二是采用Quartz 調度器實現。這是一個功能很強大的開源的專門用于定時任務調度的框架,也很好的和springboot整合,缺點:配置復雜,需要花費一定的時間去了解和研究。(本人懶,因此沒有選擇這個,但是這個功能地區強大,有時間研究)

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

因此最后選擇直接用spring自帶的task 實現。

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

直接介紹,通過頁面動態修改cron參數,修改定時規則的思路。

1 實現接口SchedulingConfigurer,這個接口只有一個方法,配置定時任務。重寫此方法,添加新的任務實現runable和新的觸發 實現trigger 。

2 在新的觸發里,把修改的cron寫入新的觸發

3 寫UI 方法,接收前端修改的定時參數。

代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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 * * * * ?";
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();//異步定時生成文件
System.out.println("Msg:定時生成文件成功");
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
System.out.println("Error:定時生成文件錯誤");
}
}
}, 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);// 定時任務觸發,可修改定時任務的執行周期
Date nextExecDate = trigger.nextExecutionTime(triggerContext);
return nextExecDate;
}
});
System.out.println("can?");
}
}

這個方法可以實現 根據頁面設置動態修改定時器的cron參數,不用重啟服務。但是運行之后發現了一個缺陷,即必須在修改完之后,只有再一次到達定時任務的時間,才會調用新的觸發時間, 這就導致,頁面設置的時間并不能即時生效,這在項目中是不符合用戶的要求,于是為了解決這個bug,換了另外一種解決方法。

思路:(了解ThreadPoolTaskScheduler這個類,TaskScheduler接口的默認實現類,多線程定時任務執行。可以設置執行線程池數(默認一個線程))

1、ThreadPoolTaskScheduler 實現TaskScheduler,可以通過方法 schedule(java.lang.Runnable task, Trigger trigger),添加定時任務和觸發器。返回java.util.concurrent.ScheduledFuture<?>,future可以控制任務的開關等。

2、前端修改定時參數,在set方法中修改ThreadPoolTaskScheduler 的觸發器。

代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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<?> 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();// 異步定時生成文件
System.out.println("Msg:定時生成文件成功");
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
System.out.println("Error:定時生成文件錯誤");
}
}
}, 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);// 定時任務觸發,可修改定時任務的執行周期
Date nextExecDate = trigger.nextExecutionTime(triggerContext);
return nextExecDate;
}
});
}
 
public void stopCron() {
if (future != null) {
future.cancel(true);//取消任務調度
}
}
}

驗證可行,作個記錄,如果有認為可以調整的地方,歡迎討論!

以上這篇淺談springboot項目中定時任務如何優雅退出就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持服務器之家。

原文鏈接:https://blog.csdn.net/weixin_42395975/article/details/95946020

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 四虎影院新网址 | 毛片99| 国产精品2 | 国产一区二区三区久久精品 | 日本福利片国产午夜久久 | 亚洲图片 自拍偷拍 | 亚洲精品乱码久久久久久蜜桃 | 扒开双腿猛进入爽爽视频ai | 日本一片免费观看高清完整 | 大胆国模一区二区三区伊人 | 国产精品福利短视在线播放频 | 四虎在线精品免费高清在线 | beeg xxxx日本| 国产主播福利在线观看 | 女同久久另类99精品国产 | 日韩综合一区 | 国产精品久久久久久岛国 | 国产在视频线在精品 | 国产免费不卡视频 | 91混血大战上海双胞胎 | 日日干天天爽 | 色哟哟哟在线精品观看视频 | 欧美日韩亚洲国内综合网香蕉 | av中文字幕网免费观看 | 青青热久麻豆精品视频在线观看 | 久久精品无码一区二区日韩av | 天天综合色天天综合色sb | 美女被扣逼 | 99热资源 | 4s4s4s4s色大众影视 | 四虎影视永久在线精品免费 | 欧美 亚洲 综合 卡通 另类 区 | 好骚好紧| 男人jj视频 | 交换余生在线播放免费 | www久久精品 | 久久精品中文騷妇女内射 | 8x8x丝袜美女| hd最新国产人妖ts视频 | 舔大逼| 欧美军人男同69gay |