Golang 定时任务
2021-05-30
2021/08/23,Golang 定时任务 是讲通过 Golang 原生的 time.Ticker 实现定时任务,本文介绍的是通过第三方库 robfig/cron 实现定时任务。
概述
- 代码:robfig/cron
- 文档: https://godoc.org/github.com/robfig/cron
标准 Cron 表达式 分成 5 段:分 minutes、时 hours、日 day of month、月 month、周 day of week。
robfig/cron v2 默认支持 6 个字段(包括秒 seconds),从 v3 开始又回到默认 5 个字段(需要做以下显式声明:cron.New(cron.WithSeconds()))。
type SpecSchedule struct {
Second, Minute, Hour, Dom, Month, Dow uint64
}
使用
// Seconds field, required
cron.New(cron.WithSeconds())
// Seconds field, optional
cron.New(cron.WithParser(cron.NewParser(
cron.SecondOptional | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor,
)))
c := cron.New()
c.AddFunc("0 30 * * * *", func() { fmt.Println("Every hour on the half hour") })
c.AddFunc("@hourly", func() { fmt.Println("Every hour") })
c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty") })
c.Start()
..
// Funcs are invoked in their own goroutine, asynchronously.
...
// Funcs may also be added to a running Cron
c.AddFunc("@daily", func() { fmt.Println("Every day") })
..
// Inspect the cron job entries' next and previous run times.
inspect(c.Entries())
..
c.Stop() // Stop the scheduler (does not stop any jobs already running).
Field name | Mandatory? | Allowed values | Allowed special characters
---------- | ---------- | -------------- | --------------------------
Seconds | Yes | 0-59 | * / , -
Minutes | Yes | 0-59 | * / , -
Hours | Yes | 0-23 | * / , -
Day of month | Yes | 1-31 | * / , - ?
Month | Yes | 1-12 or JAN-DEC | * / , -
Day of week | Yes | 0-6 or SUN-SAT | * / , - ?
Entry | Description | Equivalent To
----- | ----------- | -------------
@yearly (or @annually) | Run once a year, midnight, Jan. 1st | 0 0 0 1 1 *
@monthly | Run once a month, midnight, first of month | 0 0 0 1 * *
@weekly | Run once a week, midnight between Sat/Sun | 0 0 0 * * 0
@daily (or @midnight) | Run once a day, midnight | 0 0 0 * * *
@hourly | Run once an hour, beginning of hour | 0 0 * * * *
Linux 定时任务
2016-03-01
https://mp.weixin.qq.com/s/EJGz2koxuvLzJhnKXUBiAA
任何一本 Linux 书籍都会讲到 Cron 和 At 两个命令,都是定时执行命令,不同的是 at 执行一次,cron 周期性执行。
我们常见的 cron 应该是有这 3 种:
- vixie cron
- 支持 @yearly @monthly 等关键字
- @reboot 关键字,用于系统启动之后执行
- 环境变量
- crontab
- 邮件通知
- 日志记录
- 多任务并发
- anacron
- systemd-cron
cron 是一个知名的定时任务服务,大多数 UNIX / Linux 系统都已经预装了。
cron 包包含两个可执行文件:
- /usr/bin/cron 后台程序(系统服务),定期执行命令或脚本,可精确到分钟
- /usr/bin/crontab 管理定时任务(增删改查),每个用户都有自己的 crontab 文件
检查是否安装:
which crontab
cron
定时任务定义语法(分时日月周):
* * * * * command to be executed
- - - - -
| | | | |
| | | | ----- Day of the week (0 - 6) (Sunday is both 0 and 7)
| | | ------- Month (1 - 12)
| | --------- Day of the month (1 - 31)
| ----------- Hour (0 - 23)
------------- Minute (0 - 59)
比如:0 23 * * * bash /path/to/backup-script.sh 表示每晚 11 点运行一个备份脚本。
, 表示多个值
- 表示一个范围
/ 表示每隔多长时间
问题:cron 只能执行简单任务,不支持交互、错误处理、日志等功能。
crontab
crontab -e:编辑
crontab -l:列出
crontab -r:删除
脚本中可以使用 TZ 指定时区,如下(没用实操过):
TZ=UTC
0 0 * * * /path/to/backup-script.sh
配置文件
在大多数 UNIX 和类 UNIX 系统中,cron 会从多个文件中读取定时任务的配置。这些文件通常包括:
/etc/crontab 系统级,需要指定执行用户
/etc/cron.d/
/var/spool/cron/ 每个用户有一个自己名字的文件
anacron
好像 CentOS 中默认安装的是这货。
和 cron 相比,是基于时间间隔来的,也就是说发现距离上一次执行的时间超过了定义的间隔就再次执行。
cron 是指定到具体的时间点,如果那个时间点系统关机了,这个任务就错误了,不会执行。
提供 anacron,anacrontab 两个命令。
anacron [-s] [-f] [-n] [-d] [-t] [-S] [-T] [-u] [-r] [-V] [job] ...
-s:以非安静模式运行,输出详细信息。
-f:强制运行所有作业,忽略上次运行时间间隔。
-n:以模拟模式运行,输出将要运行的作业信息。
-d:以调试模式运行,输出调试信息。
-t:测试配置文件中所有作业是否可运行。
-S:停止所有正在运行的作业。
-T:将所有正在运行的作业的运行时间戳更新为当前时间。
-u:指定运行作业的用户。
-r:重新载入配置文件。
-V:显示版本信息。
cat /etc/anacrontab
# /etc/anacrontab: configuration file for anacron
# See anacron(8) and anacrontab(5) for details.
SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# the maximal random delay added to the base delay of the jobs
RANDOM_DELAY=45
# the jobs will be started during the following hours only
START_HOURS_RANGE=3-22
#period in days delay in minutes job-identifier command
1 5 cron.daily nice run-parts /etc/cron.daily
7 25 cron.weekly nice run-parts /etc/cron.weekly
@monthly 45 cron.monthly nice run-parts /etc/cron.monthly
systemd-cron
Systemd 这个大管家引入之后,当然定时任务这点小事也可以顺带着就给做了。
采用和系统服务相同格式的 unit file 来声明定时任务,可以定义依赖关系,精度到毫秒。从各方面来看都应该强大得多,但是大多数使用场景下简单的 cron 足够了,所以我还没有使用过 systemd-cron。
systemd-cron 也提供一个 crontab 命令,可以兼容原来的语法。
systemctl list-units | grep cron
cron.service loaded active running Regular background program processing daemon
anacron.timer loaded active waiting Trigger anacron every hour
测试
# ll /opt/cron_test.sh /etc/cron.d/test
-rw-r--r-- 1 root root 33 Jun 8 12:53 /etc/cron.d/test
-rwxr-xr-x 1 root root 184 Jun 8 12:52 /opt/cron_test.sh
# cat /etc/cron.d/test
* * * * * root /opt/cron_test.sh
# cat /opt/cron_test.sh
#!/bin/bash
{
echo "===================="
echo "TIME: $(date '+%F %T')"
echo "USER: $(whoami)"
echo "PWD : $(pwd)"
echo "PATH: $PATH"
} >> /tmp/cron_test.log 2>&1
# tail -f /tmp/cron_test.log
====================
TIME: 2026-06-08 12:54:01
USER: root
PWD : /root
PATH: /usr/bin:/bin
^C
# journalctl -u cron
-- No entries --
# journalctl -u crond | grep test
Jun 08 12:54:01 iZt4nerpnxvn37c1tlx3brZ CROND[1376687]: (root) CMD (/opt/cron_test.sh)
Jun 08 12:54:01 iZt4nerpnxvn37c1tlx3brZ CROND[1376686]: (root) CMDEND (/opt/cron_test.sh)
Jun 08 12:55:01 iZt4nerpnxvn37c1tlx3brZ CROND[1376701]: (root) CMD (/opt/cron_test.sh)
Jun 08 12:55:01 iZt4nerpnxvn37c1tlx3brZ CROND[1376700]: (root) CMDEND (/opt/cron_test.sh)
Jun 08 12:56:01 iZt4nerpnxvn37c1tlx3brZ CROND[1376711]: (root) CMD (/opt/cron_test.sh)
Jun 08 12:56:01 iZt4nerpnxvn37c1tlx3brZ CROND[1376710]: (root) CMDEND (/opt/cron_test.sh)
Jun 08 12:57:01 iZt4nerpnxvn37c1tlx3brZ CROND[1376750]: (root) CMD (/opt/cron_test.sh)
Jun 08 12:57:01 iZt4nerpnxvn37c1tlx3brZ CROND[1376749]: (root) CMDEND (/opt/cron_test.sh)