前言
- 傳統定時器是硬編碼。但是有的時候業務上需要不斷的調整
問題描述
- 我們開發了一個定鬧鐘的功能。這個功能肯定是定時器開發。但是這就存在一個問題這個定時是動態的。那麼我們如何實現呢?請接著看
簡介
- 定時器在開發中真的算是一種福利了。通過定時器我們省去了很多人力。我們通過定時器將一些繁瑣定期的事情通過程式碼去完成。在Java開發中我們通過
Timer
類可以簡單實現定時器功能。既然是springboot課程今天我們就來看看srpingboot整合定時器的事情
傳統定時器
- 這裡使用的是之前課程一的配置。springboot打算是系列講解。所以配置都是承前啟後的。建議大家按順序觀看。
@Component
public class SimpleSchedule {
@Autowired
TestMapper testMapper;
@Scheduled(cron = "*/6 * * * * ?")
private void process() {
List<test> tests = testMapper.getTests();
System.out.println(tests);
}
}
- 定時器的編寫也很簡單,只需要在類或者方法上加上@Scheduled註解。然後配置cron表示式就可以了。這裡得注意一下需要在spirngboot啟動類上加上開發定時器的註解。
@SpringBootApplication
public class CrontabApplication {
public static void main(String[] args) {
SpringApplication.run(CrontabApplication.class, args);
}
}
- 程式碼中我們使用的是最簡單的一種方式。
- cron表示式:指定任務在特定時間執行
- fixedDelay:表示上一次任務執行完成後多久再執行,引數型別long,單位:ms
- fixedDelayString:與fixedDelay一樣,只是引數型別是String
- fixedRate:表示按一定的頻率執行任務,引數型別long,單位:ms 如: fixedRate(5000),表示這個定時器任務每5秒執行一次
- fixedRateString:與fixedRate一樣,只是引數型別變為String
- initialDelay:表示延遲多久再第一次執行任務,引數型別為long ,單位:ms
- initialDelayString:與initialDelay一樣,只是引數型別String
動態定時器
- 上面的定時器已經成功的配置了。但是現在有一個需求客戶想通過頁面定製配置定時器執行的頻率。上面程式碼我們是寫死6S執行一次。如果客戶想通過視覺化配置。配置完成之後我總不能在手動改寫程式碼吧。那麼動態定時器就產生了。
V1.0
- 既然動態我們就得將客戶配置的資料進行本地化。當然是儲存在資料庫中。
- 對應的我們新建Mapper查詢定時任務資訊。因為這裡只配置了表示式。沒有配置表示式對應的定時器。也是為了測試。這裡預設表示式就是一個。
@Configuration
public class ScheduleConfigV1 implements SchedulingConfigurer {
@Autowired
CronMapper cronMapper;
@Autowired
TestMapper testMapper;
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
scheduledTaskRegistrar.addTriggerTask(()-> {
System.out.println("執行定時器任務:" + LocalDateTime.now().toLocalTime());
List<test> tests = testMapper.getTests();
System.out.println(tests);
},
triggerContext -> {
List<cron> crons = cronMapper.getCron();
Cron cron = crons.get(0);
return new CronTrigger(cron.getCron()).nextExecutionTime(triggerContext);
});
}
}
- 執行這個程式碼我們最好先關掉前面那個靜態的定時器。這樣看的效果明顯點。首先資料庫配置的是6秒執行一次。然後把資料改成2秒執行一次。看看效果。
-
我們發現只要資料庫資訊修改了。定時任務會自動修改頻率的。最重要的是不需要重啟我們的程式碼。
-
上面雖然是動態配置了。但是有一個缺點。就是修改之後生效是在下一次出發定時器執行後有效。說白了就是一開始一小時執行一次,在這期間修改了不能立馬生效必須得到下一次一小時才會去重新整理配置。這裡的動態可以理解成懶動態。
V2.0
-
上面的功能雖然是動態的。但是對於量產的話肯定是不科學的。首先資料庫不可能只存一條資料的。
-
如果存多條資料那麼多條定時規則與具體的定時器這麼進行匹配呢?
-
既然是動態的那麼如何通過資料庫控制定時器的開關呢?
-
定時任務的通過程式碼啟動實際是
scheduler.schedule(task, new CronTrigger("*/2 * * * * ?"));
實現的。這個方法返回的物件是ScheduledFuture
。通過canel方法取消定時任務。基於這兩個方法我們來改進下我們之前的定時任務。
Registar
- 首先我們提供一個註冊器,註冊器的功能就是管理定時任務。提供增加刪除功能。在增加定時器的節點上我們呼叫
scheduler.schedule(task, new CronTrigger("*/2 * * * * ?"));
來啟動定時任務。在刪除節點上呼叫之前獲取的ScheduledFuture
來canel這個定時任務。這樣做的好處我們可以隨時控制定時任務的開關
public void addCronTask(Runnable task, String cron) {
addCronTask(new CronTask(task,cron));
}
- 上面新增需要有一個runnable和cron表示式。用一個ConcurrentHashMap來管理新增進來的runnable。runnable為key,ScheduledTask為值。
public ScheduledTask scheduleCronTask(CronTask cronTask) {
ScheduledTask scheduledTask;
scheduledTask = new ScheduledTask();
scheduledTask.future = this.taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger());
return scheduledTask;
}
- 這樣構建一個ScheduledTask物件。
public final class ScheduledTask {
public volatile ScheduledFuture<!--?--> future;
/**
* 取消定時任務
*/
public void cancel() {
ScheduledFuture<!--?--> future = this.future;
if (future != null) {
future.cancel(true);
}
}
}
- 這樣我們就可以通過構建一個runnable執行緒,結合表示式通過註冊器註冊就可以開啟這個執行緒已固定頻率執行。通過remove關閉執行緒。
SchedulingRunnable task = new SchedulingRunnable(TestMapper.class, "getTests", null);
cronTaskRegistrar.addCronTask(task, "0/10 * * * * ?");
- 這樣做的好處是我們可以在表資料修改的情況下立馬更新定時任務規則。
總結
-上面的程式碼已經上傳值gitee
點我傳送
https://gitee.com/zxhTom/crontab.git
-
下面Java類使我們這次使用用到的類。想深入瞭解的私信我。
-
SchedulingConfigurer
-
DisposableBean
-
ConcurrentHashMap