只要是後端開發,多多少少都會有一些撰寫定時任務的需求,像是每天晚上 10 點固定處理報表、或是每 30 分鐘處理一批次的任務…等等,因此這篇文章我們就來介紹一下,這個「定時任務」的語法到底要怎麼寫吧!
所謂的 Cron,其實是 Linux 系統下的一個定時任務管理服務,但是因為 Cron 的表達式實在是太萬用了,所以目前也很廣泛用在 GitHub Actions、Spring Boot 的 @Scheduled
…等框架上。
舉例來說,只要大家有看過類似的表達式 - cron: "0 21 * * *"
或是 @Scheduled(cron = "0 0 21 * * *")
的寫法,裡面的一堆莫名其妙的 *
,其實就是 Cron 的「定時任務」的寫法!
因此像是在上面這兩個例子中,"0 21 * * *"
指的其實就是「每天晚上 9 點整執行一次這個任務」的意思。
不過也因為 Cron 的表達式實在是有點抽象,所以下面我們就來介紹一下 Cron 中的每一個 *
代表的意義,來徹底了解一下如何使用 Cron 寫出我們想要的時間吧!
*
)
#
在 Cron 中,最傳統且常見的表達式為「5 個 *
」,不過因為 Spring Boot 本身的設計不太一樣,所以 Spring Boot 會採用「6 個 *
」 的方式來撰寫(因此大家如果不是寫 Java/Spring Boot 的話,就只要看上半部分的 Cron 傳統寫法就好,但如果是寫 Java/Spring Boot 的話,建議也要把下面的補充部分看完)。
而如果要寫出 Cron 的傳統表達式(5 個 *
的版本),首先必須要先寫出 5 個 *
,並且每個 *
之間要用一個空白鍵隔開,所以最基本的 Cron 表達式就會像是下面這個樣子:
* * * * *
而這 5 個 *
,他們所站的每一個「位置」,就會代表不同的意思,譬如說左邊數過來第一個 *
是表示「分鐘」的意思,左邊數過來第二個 *
是表示「小時」的意思…以此類推,因此這 5 個 *
的實際意義,可以用下面這張圖來表示:
所以舉例來說,假設 Cron 表達式為 0 16 * * *
,那就是表示「我們指定在每天的 16:00」要執行一次這個任務(因為我們設定了第一個 *
為 0,第二個 *
為 16,所以就是表示我們指定要在分鐘數為 0、並且小時數為 16 的時候執行任務,因此 0 16 * * *
的結果才會是在每天的 16:00 執行任務)。
再舉一個例子,假設 Cron 的表達式為 23 1 * * *
,就是表示我們指定在「每天的凌晨 1 點 23 分」要執行這個任務。
再再舉一個例子,假設 Cron 的表達式為 59 23 * * *
,就是表示我們指定要在「每天的晚上 23:59 分」執行任務。
所以對於 Cron 表達式而言,只要你將某一個 *
改為一個確切的數字,其實就是指定要在「那個時間點」執行任務,就只是這樣子而已!!Magic!!
不過另外也補充一下,除了有被修改過的數字,其他維持原樣的 *
則是表示「所有」的意思,所以像是 23 1 * * *
,就只有前面的 23 1
的「凌晨 1 點 23 分」是固定的,其他的 * * *
則是表示「所有」,也就是在「所有日期、所有月份、所有星期」底下的「凌晨 1 點 23 分」,都會執行當前這個任務,因此 23 1 * * *
才會被解讀成「每天的凌晨 1 點 23 分」。
下面再舉更多的例子給大家參考:
Cron 表達式 |
實際意義 |
備註 |
---|---|---|
0 21 * * * |
在每天的晚上 21:00 執行一次任務 | |
* * * * * |
每分鐘都要執行一次任務 | |
0 * * * * |
在每個小時的 0 分執行一次任務(ex: 1 點 0 分、2 點 0 分…. 23 點 0 分) | |
0 0 * * 5 |
在每週五的 0 點 0 分執行任務 | 但一般會避免寫 0 點 0 分這種數字,因為 0 點 0 分是一天的開始,所以如果是要進行那種「當天晚上結算的任務」,改成 23:59 會比較好 |
59 23 * * 0 |
在每週日的 23 點 59 分執行任務 | 注意 0 是週日(Sunday),1-6 是週一到週六 |
30 10 1 * * |
在每個月的 1 號的早上 10:30 分執行一次任務 |
所以透過上面的例子,我們就可以指定「現在是想要在哪一天、哪一個小時、哪一分」,去執行我們想要執行的任務了!
補充:如果想要自己玩玩看 Cron 表達式,也可以到 Crontab.guru 這個網站玩一下,他不僅有基礎教學,還可以檢測你寫的 Cron 表達式是否正確,因此也很推薦大家在撰寫 Cron 表達式時先丟上來檢查一下,確認沒問題之後再寫進程式裡面。
不過,Cron 除了有上述的常見用法之外,Cron 其實有更進階的用法,但以下這些用法其實我自己也用的不多,所以建議大家參考就好。
舉例來說,在上面的 Cron 的基本用法中,當我們寫 30 10 * * *
時,就是要在「每天的 10:30」執行任務,很直覺對吧,但是如果我們寫成 */3 10 * * *
時,就是表示要在「每天的 10 點 0 分、10 點 3 分、10 點 6 分、10 點 9 分…(不斷加三分鐘)…、10 點 54 分、10 點 57 分」執行任務。
除此之外,我們也可以改寫成是 30 10-15 * * *
,則是表示要在「每天的 10:30、11:30、12:30、13:30、14:30、15:30」執行任務。
再或者,我們也可以改寫成是 30 10,23 * * *
,表示要在「每天的 10:30 和 23:30」執行任務(這個好用)。
所以在 Cron 的進階用法中,已經進展到「不只是指定某個特定的時間點了」,而是變成「指定一段時間區間」或是「指定時間的變動方式」了,因此大家如果有比較特殊的需求,就可以研究一下 Cron 的進階用法,寫出最符合你想要的時間表達式。
這邊也是舉更多的例子給大家參考,或是大家也可以直接到 Crontab.guru 上測試,他也有支援進階的用法。
Cron 表達式 | 實際意義 |
---|---|
0 22 * * 1-5 |
在週一到週五的每天晚上 22:00 執行任務 |
23 12-20/2 * * * |
在每天的 12:23、14:23、16:23、18:23、20:23 執行任務 |
30 10,11 * * 0 |
在每週日的 10:30 和 11:30 執行任務 |
59 23 1 */2 * |
在每個奇數月(1、3、5、7、9、11)的 1 號的 23:59 執行任務 |
所以總結上面的介紹的話,Cron 原本是 Linux 中的一個定時任務管理工具,但是因為他的表達式實在是太好用,所以現在很廣泛使用的定時任務的表達上面。
而常見的 Cron 為 5 位數(預設是 5 個 *
),每一個位數分別代表了「分鐘」、「小時」、「日期」、「月份」、「星期幾」的含義,因此大家就可以透過修改不同位數的值,用來表達你想要在哪個時間點執行任務了!
*
)
#
在 Spring Boot 中,如果我們想要使用 @Scheduled
創建一個定時任務的話,則是要使用 「6 個 *
」 來撰寫(注意是 6 個!!6 個!!!!),而這個多出來的一個 *
,其實就只是往左擴充一個「秒數」的功能而已。
所以在 Spring Boot 中的 Cron 表達式,就會是下面這個樣子:
因此在 Spring Boot 中,要實作 @Scheduled
時,就要先寫出 6 個 *
,然後才是根據不同的位置,去指定不同的時間點,而這部分其實就跟上面的 Cron 表達式一模一樣,所以只要 5 個 *
的版本有學好,6 個 *
版本就只是往左多擴充一個「秒數」部分而已!
所以像是我們可以寫 @Scheduled(cron = "11 59 23 * * *")
,就是表示要在「每天的 23 點 59 分 11 秒(23:59:11)」執行任務,而當我們寫 @Scheduled(cron = "* 0 10 * * *")
時,則是要在「每天的 10:00:00、10:00:01、10:00:02…(不斷加一秒鐘)…、10:00:58、10:00:59」執行任務。
也因為 Spring Boot 所使用的 Cron 表達式是 6 位數,因此在實作 Spring Boot 時,就要特別注意 Cron 的最左邊是「秒數」,所以千萬不要直接複製網路上常見的 Cron(5 位數)到 Spring Boot,Spring Boot 會毀滅的🥹(最常見的作法就是把最左邊那一位補 0
,表示要固定在 0 秒運行,這樣就和 5 位數的 Cron 表達式運行的時機點一樣了)。
這篇文章我們介紹了 Cron 的表達式的寫法,並且也補充了 Spring Boot 中的 Cron 的寫法,希望讓大家對於 Cron 有更多的了解。
如果你對後端技術有興趣的話,也歡迎免費訂閱 《古古的後端筆記》電子報 ,每週二為你送上一篇後端技術分享,那我們就下一篇文章見啦!
補充:我開設的 Spring Boot 零基礎入門 、 Spring Security 零基礎入門 、 GitHub 免費架站術 已在 Hahow 平台上架啦!輸入折扣碼「HH202503KU」即可享 85 折優惠。