Crontab是linux下定時調度配置文件,通過它,我們可以讓系統的程序、腳本、命令、任務按設定的時間、間隔、周期循環的運行。 在Crontab里時間粒度最小的是分鐘。也就是說,通過Crontab配置,我們最多可以讓目標任務每分鐘執行一次,更頻繁的執行是不行的,只能借助其它方法。
比如說,如果希望一個程序每30秒執行一次,該怎么辦呢?
變通的方法還是有的。 一種思路是,在Crontab里添加兩條配置,一條是正常調度,每分鐘執行一次,另一條是等待30秒后才執行。
# Need these to run on 30-sec boundaries, keep commands in sync.
* * * * * /path/to/executable param1 param2
* * * * * ( sleep 30 ; /path/to/executable param1 param2 )
這種方法感覺有點生硬,怪怪的,但的確可行。這種方法實際是可以簡寫成一行:
* * * * * /bin/bash -l -c "/path/to/executable; sleep 30 ; /path/to/executable"
還有一種方法是使用watch命令:
$ watch --interval .30 script_to_run_every_30_sec.sh
但watch是命令行工具,我們可以使用nohup命令讓它在后臺運行。
SystemD定時器
如果我們使用的linux系統里有SystemD,可以使用SystemD定時器在任何時間粒度上調度程序,理論上可以小到納秒級別——當然,這樣做有點瘋狂??傊?,它在任務調度上的靈活性遠比Cron要高——無需使用sleep這種蹩腳的方案。
比起一行完成配置的crontab來說,建立一個SystemD定時器會顯得稍微復雜一些,但為了更好的實現小于‘每分鐘’粒度的調度任務,這種方法值得嘗試。
SystemD定時器實現原理簡單說就是兩部分:一個系統service,一個SystemD定時器。SystemD定時器執行調度,而任務是寫在service里。
下面有個簡單的例子,目標是讓系統logger每十秒鐘輸出一次“Hello World”;
/etc/systemd/system/helloworld.service
[Unit]
Description=Say Hello
[Service]
ExecStart=/usr/bin/logger -i Hello World
/etc/systemd/system/helloworld.timer
[Unit]
Description=Say Hello every 10 seconds
[Timer]
OnBootSec=10
OnUnitActiveSec=10
AccuracySec=1ms
[Install]
WantedBy=timers.targethelloworld.timer里并沒有聲明service的名稱,那它和service是如何關聯的呢?沒錯,因為它們的名稱相同,都是helloworld。
如果想讓整個系統使用這個定時器,這兩個文件就需要放置在/etc/systemd/system下。如果想給某個用戶使用,則放置在~/.config/systemd/user。想讓這個定時器立即運行,需要執行下面的命令:
systemctl enable --now helloworld.timer
里面的–now標記是讓定時器立即執行。否則,只有等系統重啟后,或者用戶登錄是才會觸發運行。
[Timer]部分里的各個字段的作用如下:
OnBootSec – 系統啟動多少秒后開始執行調度
OnUnitActiveSec – 重復調度相關service的時間間隔。就是這行配置決定了跟cron job一樣定時調度的動作。
AccuracySec – 定時器精度。 默認是一分鐘,跟cron很相似??梢砸蟮母?,但精度增加會帶來更多系統的消耗,更頻繁的喚醒CPU。上面的配置里寫的是1ms,顯然不是個聰明的決定。通常我們可以把它設置為 1(1秒),對于我們這樣低于1分鐘時間粒度的定時器的精度要求已經夠用了。也是因為如此,我們會看到,實際程序運行時輸出“Hello World”消息的時間經常會延遲1秒左右。如果你認為這一秒左右的延遲不是問題,那就應該這樣設定。
你會發現,SystemD定時器和Crontab定時器并不是一樣的——任務調度的周期并不是按年月日小時分鐘周期設定的,它是按我們第一次執行它的時間開始,每次追加一個周期的時間。如果我們鐘情于Crontab那樣的時間配置方式,SystemD定時器也是支持的,那就需要把OnBootSec和OnUnitActiveSec去掉,換成OnCalendar,下面是一個例子:
OnCalendar=*-*-* *:*:00,10,20,30,40,50
最后補充一點,默認情況下,SystemD定時器和service的關聯是通過相同的名稱,如果你愿意,也可以在[Timer]配置里通過指定Unit字段配對。
上面的幾種方法都可以實現低于分鐘粒度的定時調度任務。各有優點。SystemD定時器看起來更正規,但稍微復雜了一點。Crontab+sleep方式雖然別扭,但對于一些小任務來說沒有不能勝任的。