我们来看一下 crontab 的时间格式,
Spec 参考 beego toolbox 模块下的 crontab 的组成格式:
- // 前 6 个字段分别表示:
- // 秒钟: 0-59
- // 分钟: 0-59
- // 小时: 1-23
- // 日期: 1-31
- // 月份: 1-12
- // 星期: 0-6(0 表示周日)
- // 还可以用一些特殊符号:
- //*: 表示任何时刻
- // ,: 表示分割, 如第三段里: 2,4, 表示 2 点和 4 点执行
- // -: 表示一个段, 如第三端里: 1-5, 就表示 1 到 5 点
- // /n : 表示每个 n 的单位执行一次, 如第三段里,*/1, 就表示每隔 1 个小时执行一次命令. 也可以写成 1-23/1.
- /////////////////////////////////////////////////////////
- // 0/30 * * * * * 每 30 秒 执行
- // 0 43 21 * * * 21:43 执行
- // 0 15 05 * * * 05:15 执行
- // 0 0 17 * * * 17:00 执行
- // 0 0 17 * * 1 每周一的 17:00 执行
- // 0 0,10 17 * * 0,2,3 每周日, 周二, 周三的 17:00 和 17:10 执行
- // 0 0-10 17 1 * * 毎月 1 日从 17:00 到 7:10 毎隔 1 分钟 执行
- // 0 0 0 1,15 * 1 毎月 1 日和 15 日和 一日的 0:00 执行
- // 0 42 4 1 * * 毎月 1 日的 4:42 分 执行
- // 0 0 21 * * 1-6 周一到周六 21:00 执行
- // 0 0,10,20,30,40,50 * * * * 每隔 10 分 执行
- // 0 */10 * * * * 每隔 10 分 执行
- // 0 * 1 * * * 从 1:0 到 1:59 每隔 1 分钟 执行
- // 0 0 1 * * * 1:00 执行
- // 0 0 */1 * * * 毎时 0 分 每隔 1 小时 执行
- // 0 0 * * * * 毎时 0 分 每隔 1 小时 执行
- // 0 2 8-20/3 * * * 8:02,11:02,14:02,17:02,20:02 执行
- // 0 30 5 1,15 * * 1 日 和 15 日的 5:30 执行
PS: beego 的定时模块比较强大, 支持了 秒级别 的定时任务
现在假设当前时间为 21:11:05 , 如何定义一个 每隔 5 分钟 循环运行的定时任务?
网上搜索, 大部分都是这样的方案:
0 */5 * * * *
ok, 我们运行下该定时任务, 看看会发生什么情况:
2019/10/05 21:11:05:087825 local [INFO] ------ 定时任务: [Test] 加载成功 ------ cron.go:155
2019/10/05 21:12:00:007810 local [DEBUG] 定时任务: [Test] 成功获取 Redis 分布式锁, 开始执行调度任务 delivery.go:90
2019/10/05 21:12:00:036411 local [DEBUG] 定时任务: [Test] 调度成功 ... ...
2019/10/05 21:17:00:004286 local [DEBUG] 定时任务: [Test] 成功获取 Redis 分布式锁, 开始执行调度任务 delivery.go:90
2019/10/05 21:17:00:010506 local [DEBUG] 定时任务: [Test] 调度成功 ... ...
很奇怪, 为什么任务不是 21:16:05 运行, 而是 21:12:00 就开始运行了
网上大致的解释为:
设置的 N 应该被 60 整除才行 " 的意思是: 如果 N 能被 60 整除, 则会相当于每隔 N 分钟执行一次, 一个小时正好执行 60/N 次; 如果 N 不能被 60 整除, 则在能整除和整点 (除完余数为 0) 的时候都会执行.
如何解决?
秒
秒(第一位): 替换为 当前秒 / 60, 当前秒之后每隔 60 秒执行一次;
分(只替换如下格式的):
*/?
分(第二位): 替换为当前分 / 每隔多少分, 当前分之后每隔多少分执行一次;
小时(只替换如下格式的):
*/?
小时(第三位): 替换为当前小时 / 每隔多少小时, 当前小时之后每隔多少小时执行一次;
举例: 每隔 7 分钟, 每隔 9 分钟, 每隔 14 分钟, 每隔 27 分钟
以 21:16:05 为例
- 0 */7 * * * * 替换为: 5/60 16/7 * * * *
- 0 */9 * * * * 替换为: 5/60 16/9 * * * *
- 0 */14 * * * * 替换为: 5/60 16/14 * * * *
- 0 */27 * * * * 替换为: 5/60 16/27 * * * *
代码解决方案:
- func ConvertSecond(spec string) string {
- if spec != "" {
- _spec := strings.Split(spec, " ")
- hour, minute, second := time.Now().Clock()
- // second 处理
- _second := strconv.FormatInt(int64(second), 10) + "/60"
- spec = strings.Replace(spec, _spec[0], _second, 1)
- // minute 处理(类似: */5)
- if strings.HasPrefix(_spec[1], "*/") {
- _minute := strconv.FormatInt(int64(minute), 10) + "/" + strings.Trim(_spec[1], "*/")
- spec = strings.Replace(spec, _spec[1], _minute, 1)
- }
- // hour 处理(类似: */5)
- if strings.HasPrefix(_spec[2], "*/") {
- _hour := strconv.FormatInt(int64(hour), 10) + "/" + strings.Trim(_spec[2], "*/")
- spec = strings.Replace(spec, _spec[2], _hour, 1)
- }
- return spec
- }
- return ""
- }
ok, 我们再次运行下该定时任务, 查看结果:
2019/10/05 21:18:38:333458 local [INFO] ------ 定时任务: [Test] 加载成功 ------ cron.go:155
2019/10/05 21:23:38:002390 local [DEBUG] 定时任务: [Test] 成功获取 Redis 分布式锁, 开始执行调度任务 delivery.go:90
2019/10/05 21:23:38:009905 local [DEBUG] 定时任务: [Test] 调度成功 ... ...
2019/10/05 21:28:38:008273 local [DEBUG] 定时任务: [Test] 成功获取 Redis 分布式锁, 开始执行调度任务 delivery.go:90
2019/10/05 21:28:38:011781 local [DEBUG] 定时任务: [Test] 调度成功 ... ...
符合预期
来源: http://www.tuicool.com/articles/yquIzyf