SpringBoot的@Scheduled和@Schedules區(qū)別小結
@Scheduled 的詳細解析
參數詳解
cron: 使用Cron表達式來指定復雜的調度模式。Cron表達式的格式如下:
- 秒(0-59)
- 分鐘(0-59)
- 小時(0-23)
- 日(1-31)
- 月(1-12 或 JAN-DEC)
- 星期(0-7 或 SUN-SAT,其中0和7都表示星期日)
- 年(可選,1970-2099)
Cron表達式的每個字段可以是具體的值、范圍、列表或通配符(*)。例如:
"0 0 12 * * ?"表示每天中午12點。"0 15 10 ? * MON-FRI"表示周一至周五上午10:15執(zhí)行。"0 0/5 * * * ?"表示每5分鐘執(zhí)行一次。"0 0 12 1 * ?"表示每月第一天中午12點執(zhí)行。
fixedRate: 指定以固定的速率重復執(zhí)行任務,從前一次任務開始時刻算起。它不會等待前一個任務完成,因此如果任務執(zhí)行時間超過了設定的時間間隔,可能會有重疊的任務實例在運行。
fixedDelay: 類似于
fixedRate,但是它是以前一次任務的完成時刻作為下一次任務啟動的時間基準。這種方式可以確保每次只有一個任務實例在運行,前提是任務的執(zhí)行時間短于延遲時間。initialDelay: 在第一次執(zhí)行之前等待的時間(毫秒)。這個參數通常與
fixedRate或fixedDelay一起使用,用來設置首次執(zhí)行前的延遲。zone: 定義時區(qū),默認是系統(tǒng)的默認時區(qū)。如果你的應用需要在全球不同地區(qū)運行,明確指定時區(qū)可能是很重要的。
Cron表達式、fixedRate、fixedDelay、initialDelay如何選擇
- 如果你需要非常具體的調度模式,如每天凌晨兩點執(zhí)行某個任務,那么應該選擇Cron表達式。
- 如果你希望任務以固定的速率重復執(zhí)行,不論每次執(zhí)行花費多少時間,你應該選擇
fixedRate。 - 如果你希望在前一個任務完全結束后再等待一段固定時間才開始下一個任務,那么
fixedDelay是更好的選擇。 - 如果你需要設置首次執(zhí)行的延遲,可以添加
initialDelay參數到你的調度配置中。
示例代碼
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class ScheduledTasks {
// 每天中午12點執(zhí)行(上海時區(qū))
@Scheduled(cron = "0 0 12 * * ?", zone = "Asia/Shanghai")
public void scheduledTaskUsingCron() {
System.out.println("Scheduled task using cron at Asia/Shanghai timezone.");
}
// 每5秒執(zhí)行一次,首次執(zhí)行前等待2秒
@Scheduled(fixedRate = 5000, initialDelay = 2000)
public void scheduledTaskWithFixedRate() {
System.out.println("Scheduled task with fixed rate.");
}
// 上次任務完成后等待3秒再執(zhí)行下一次
@Scheduled(fixedDelay = 3000)
public void scheduledTaskWithFixedDelay() {
System.out.println("Scheduled task with fixed delay.");
}
}@Schedules 的詳細解析
@Schedules 允許多個 @Scheduled 注解組合在一起,為同一個方法設定多種不同的調度策略。這對于那些需要在多個不同時間點或條件下觸發(fā)的方法非常有用。
示例代碼
import org.springframework.scheduling.annotation.Schedules;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class MultipleScheduledTasks {
// 每天中午12點執(zhí)行,并且每5秒也執(zhí)行一次
@Schedules({
@Scheduled(cron = "0 0 12 * * ?"),
@Scheduled(fixedRate = 5000)
})
public void multipleScheduledTasks() {
System.out.println("Multiple scheduled tasks.");
}
}啟用和管理定時任務
要使這些注解生效,你需要確保你的Spring應用已經啟用了對它們的支持。這可以通過在配置類上添加 @EnableScheduling 來實現:
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
@Configuration
@EnableScheduling
public class SchedulingConfig {
// 配置類內容
}自定義 TaskScheduler
對于更復雜的需求,比如調整線程池大小或者設置線程名稱前綴等,你可以通過自定義 TaskScheduler 來進行配置。Spring提供了幾種內置的調度器實現,如 ThreadPoolTaskScheduler 和 ConcurrentTaskScheduler。
示例代碼
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
@Configuration
public class SchedulerConfig {
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(10); // 設置線程池大小
taskScheduler.setThreadNamePrefix("MyScheduledTask-");
taskScheduler.setErrorHandler(t -> {
System.err.println("Error occurred in scheduled task: " + t.getMessage());
});
taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
taskScheduler.setAwaitTerminationSeconds(60);
return taskScheduler;
}
}錯誤處理
當一個預定任務拋出異常時,默認情況下Spring會記錄錯誤日志,但任務本身不會被取消。如果你想改變這種行為,可以使用 DelegatingErrorHandlingRunnable 或者直接在 ThreadPoolTaskScheduler 中設置錯誤處理器(如上面的示例所示)。
自定義錯誤處理邏輯
你可以創(chuàng)建自己的錯誤處理器來捕獲并處理異常:
import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@Configuration
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("Async-");
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler() {
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
// 自定義異常處理邏輯
System.err.println("Exception in async task: " + ex.getMessage());
}
};
}
}最佳實踐
避免長時間運行的任務:盡量不要讓預定任務執(zhí)行過長的時間,因為它們可能會阻塞其他任務的執(zhí)行。如果任務有可能運行很長時間,請考慮將其拆分為更小的部分,或者使用異步處理。
任務沖突管理:當使用
fixedRate時,要注意任務可能會重疊。如果任務執(zhí)行時間可能超過間隔時間,應該選擇fixedDelay來避免這種情況。資源清理:確保在任務結束時正確釋放任何獲取的資源,比如數據庫連接或文件句柄。
監(jiān)控和報警:建立適當的監(jiān)控和報警機制,以便在任務失敗時能夠及時收到通知并采取行動??梢岳肧pring Boot Actuator提供的健康檢查端點,或者集成第三方監(jiān)控工具如Prometheus、Grafana等。
冪等性設計:確保任務邏輯具有冪等性,即多次執(zhí)行相同的任務不會導致不一致的結果。這在分布式環(huán)境中尤為重要。
日志記錄:為每個任務添加詳細的日志記錄,包括任務開始時間和結束時間,以便追蹤任務執(zhí)行情況。
測試:編寫單元測試和集成測試來驗證定時任務的行為是否符合預期??梢允褂肕ockito或其他測試框架模擬依賴服務。
多實例部署的問題:在多實例部署的情況下,所有的實例都會嘗試執(zhí)行相同的定時任務,這可能導致數據競爭或重復執(zhí)行。一種解決方案是使用分布式鎖,如Redisson提供的RedLock,來保證同一時間只有一個實例執(zhí)行特定的任務。
性能優(yōu)化:對于高并發(fā)場景下的定時任務,應該評估線程池的大小和任務的執(zhí)行頻率,避免因過多的任務同時啟動而導致資源耗盡??梢酝ㄟ^限流、隊列管理和異步處理等方式提高系統(tǒng)的穩(wěn)定性和響應速度。
處理定時任務中的常見問題
任務未按預期執(zhí)行:檢查日志以確定是否有任何異?;蝈e誤信息。確保任務方法是非靜態(tài)的,并且沒有被final修飾。確認
@EnableScheduling已經正確啟用。另外,檢查是否存在其他因素阻止任務執(zhí)行,如網絡延遲或依賴服務不可用。任務執(zhí)行順序混亂:如果你有多個任務幾乎同時執(zhí)行,可能會出現執(zhí)行順序混亂的情況。確保你理解了
fixedRate和fixedDelay的區(qū)別,并根據需要選擇合適的方式。此外,可以通過增加任務之間的最小間隔時間來減少沖突的可能性。多實例部署的問題:在多實例部署的情況下,所有實例都會嘗試執(zhí)行相同的定時任務。為了解決這個問題,可以引入分布式鎖機制,如基于Redis的鎖或Zookeeper的臨時節(jié)點,以確保同一時間只有一個實例執(zhí)行任務。
長時間運行的任務:盡量不要讓預定任務執(zhí)行過長的時間,因為它們可能會阻塞其他任務的執(zhí)行。如果任務有可能運行很長時間,請考慮將其拆分為更小的部分,或者使用異步處理,如通過消息隊列(MQ)來分發(fā)任務。
時區(qū)問題:確保你的應用程序正確處理時區(qū)差異,特別是在全球范圍內運行時??梢栽?nbsp;
@Scheduled注解中顯式指定zone參數,或者在整個應用程序中統(tǒng)一配置默認時區(qū)。
案例分析
假設你正在開發(fā)一個電子商務平臺,需要每天凌晨2點生成前一天的銷售報告。你可以使用 @Scheduled 注解來安排這個任務:
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@Service
public class DailyReportService {
@Scheduled(cron = "0 0 2 * * ?", zone = "Asia/Shanghai")
public void generateDailySalesReport() {
// 執(zhí)行生成銷售報告的邏輯
System.out.println("Generating daily sales report at Asia/Shanghai timezone.");
}
}此外,你還可以結合上述的最佳實踐來增強任務的可靠性,例如:
- 確保任務具有冪等性,即使由于某種原因任務重復執(zhí)行也不會影響結果。
- 添加詳細的日志記錄,幫助追蹤任務的執(zhí)行情況。
- 實現錯誤處理邏輯,確保即使發(fā)生異常也能得到妥善處理。
- 如果平臺有多實例部署,考慮使用分布式鎖來防止多個實例同時生成報告。
到此這篇關于SpringBoot的@Scheduled和@Schedules區(qū)別小結的文章就介紹到這了,更多相關SpringBoot @Scheduled和@Schedules內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot使用自動配置xxxAutoConfiguration
這篇文章介紹了SpringBoot自動配置xxxAutoConfiguration的使用方法,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-12-12
Java非靜態(tài)成員變量之死循環(huán)(詳解)
下面小編就為大家?guī)硪黄狫ava非靜態(tài)成員變量之死循環(huán)(詳解)。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-09-09
httpclient staleConnectionCheckEnabled獲取連接流程解析
這篇文章主要為大家介紹了httpclient staleConnectionCheckEnabled獲取連接流程示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11

