最近在用SpringBoot寫(xiě)一個(gè)關(guān)于定時(shí)項(xiàng)目的時(shí)候遇到一個(gè)問(wèn)題,就是客戶端訪問(wèn)服務(wù)器的結(jié)果實(shí)際上是每個(gè)一段時(shí)間發(fā)生一次變化,并且在服務(wù)器在每天的某個(gè)固定的時(shí)間點(diǎn)都要觸發(fā)一次事件。
我們當(dāng)然可以在遇到每一個(gè)請(qǐng)求時(shí)都重新計(jì)算結(jié)果,但是為了提高效率,我們顯然可以讓服務(wù)器每隔一段時(shí)間計(jì)算一次結(jié)果,并且把這個(gè)結(jié)果進(jìn)行保存,對(duì)在下一個(gè)時(shí)間段內(nèi)的每個(gè)請(qǐng)求都直接返回計(jì)算后的結(jié)果。這樣就能較好的提高了服務(wù)器的性能。
那么問(wèn)題就在于如何處理定時(shí)任務(wù)。其實(shí)SpringBoot早就提供了非常方便的接口,但是網(wǎng)上的介紹還是有點(diǎn)亂的,我就記錄下具體操作的注意點(diǎn)方便以后查找。
創(chuàng)建定時(shí)服務(wù)
一般來(lái)說(shuō)定時(shí)服務(wù)會(huì)寫(xiě)在一個(gè)Component里,方便管理。對(duì)于定時(shí)任務(wù),我們其實(shí)只要在需要定時(shí)執(zhí)行的函數(shù)前加上 @Scheduled 注解,比如下面這樣:
@Component public class ScheduledTask{ @Scheduled(fixedRate = 5000) public void scheduledTask(){ //do sth... } }
與此同時(shí),我們還要在項(xiàng)目的啟動(dòng)文件里配置上 @EnableScheduling 注解,告訴項(xiàng)目我們是支持定時(shí)任務(wù)的:
@SpringBootApplication @EnableScheduling public class LotteryApplication{ public static void main(String[] args){ SpringApplication.run(LotteryApplication.class, args); } }
這樣我們的函數(shù)就能定時(shí)執(zhí)行了。
Scheduled參數(shù)
Scheduled主要支持 fixRate , fixDelay , cron , initialDelay 這些參數(shù),下面做簡(jiǎn)要說(shuō)明。
fixRate和fixDelay
fixRate和fixDelay參數(shù)都指定了函數(shù)每隔某個(gè)毫秒數(shù)執(zhí)行一次,但是他們之間也有細(xì)小的差別。
fixRate
fixRate的計(jì)時(shí)是相對(duì)于系統(tǒng)時(shí)間的,也就是一定相隔會(huì)固定時(shí)間執(zhí)行。
fixDelay
fixDelay的計(jì)時(shí)是相對(duì)于上一次調(diào)用的時(shí)間的,因此他受其他程序調(diào)用的影響,如果該函數(shù)在其他地方被手動(dòng)調(diào)用,那么這個(gè)計(jì)時(shí)器就會(huì)重新計(jì)時(shí)。
initialDelay參數(shù)
initialDelay參數(shù)是個(gè)額外參數(shù),比較簡(jiǎn)單,就是指定從項(xiàng)目開(kāi)始運(yùn)行到該函數(shù)首次被調(diào)用的執(zhí)行時(shí)間,以毫秒計(jì)。
如果不指定這個(gè)參數(shù),這個(gè)值就是-1,也就是程序開(kāi)始時(shí)不執(zhí)行。
在不知到initialDelay這個(gè)參數(shù)的時(shí)候?yàn)榱俗尦绦騿?dòng)時(shí)立即調(diào)用該函數(shù),我讓這個(gè)定時(shí)類繼承了 InitializingBean 并在重寫(xiě)的 afterPropertiesSet 方法中手動(dòng)調(diào)用了這個(gè)函數(shù)。?!,F(xiàn)在看起來(lái)還真是愚蠢。。。
cron
這個(gè)Cron是最復(fù)雜也是高度自定義化的定時(shí)工具,在Linux系統(tǒng)里也有類似的crontab命令。他其實(shí)是更加細(xì)致的定義了定時(shí)任務(wù),以一個(gè)字符串的形式進(jìn)行表示。
在SpringBoot中,一個(gè)cron字符串是由六個(gè)部分以空格組成的字符串,文檔中的例子是這樣的:
"0 0 * * * *" = the top of every hour of every day. "*/10 * * * * *" = every ten seconds. "0 0 8-10 * * *" = 8, 9 and 10 o'clock of every day. "0 0 6,19 * * *" = 6:00 AM and 7:00 PM every day. "0 0/30 8-10 * * *" = 8:00, 8:30, 9:00, 9:30, 10:00 and 10:30 every day. "0 0 9-17 * * MON-FRI" = on the hour nine-to-five weekdays "0 0 0 25 12 ?" = every Christmas Day at midnight
六個(gè)部分分別表示秒、分、時(shí)、日、月、周。
他支持'-‘表示范圍,'*'表示通配,'/‘表示在左邊的時(shí)間匹配后間隔右邊的時(shí)間,'?'一般表示周的通配。
具體的使用方法還要參考相關(guān)文檔。
網(wǎng)上有很多類似 Cron表達(dá)式測(cè)試工具 這樣的測(cè)試工具,方便我們測(cè)試自己寫(xiě)的Cron表達(dá)式。
下面一個(gè)例子表示每隔五分鐘執(zhí)行一次:
@Scheduled(cron = "0 0/5 * * * ?") public void fiveMinutes(){ //do sth. }
注意點(diǎn)
在實(shí)際使用Scheduled注解時(shí),我們一定要指定且僅僅指定fixRate、fixDelay、cron當(dāng)中的一個(gè),否則肯定會(huì)導(dǎo)致錯(cuò)誤。當(dāng)然,我們可以任意指定initialDelay參數(shù)。