golang高效Cron表达式解析与任务调度插件gronx的使用
Golang高效Cron表达式解析与任务调度插件gronx的使用
gronx
是一个Golang的cron表达式解析器,具有任务运行器和守护进程功能,支持类似crontab的任务列表文件。它可以从任何时间点找到表达式下一次(NextTick()
)或上一次(PrevTick()
)的运行时间。
主要特性
- 零依赖
- 非常快速,因为它在段不匹配时会提前退出
- 内置类似crontab的守护进程
- 支持秒级时间粒度
安装
go get -u github.com/adhocore/gronx
基本用法
import (
"time"
"github.com/adhocore/gronx"
)
gron := gronx.New()
expr := "* * * * *"
// 检查表达式是否有效,返回bool
gron.IsValid(expr) // true
// 检查表达式是否在当前时间到期,返回bool和error
gron.IsDue(expr) // true|false, nil
// 检查表达式在给定时间是否到期
gron.IsDue(expr, time.Date(2021, time.April, 1, 1, 1, 0, 0, time.UTC)) // true|false, nil
也可以不实例化直接检查有效性:
import "github.com/adhocore/gronx"
gronx.IsValid("* * * * *") // true
批量到期检查
如果有多个cron表达式需要在同一参考时间检查是否到期,可以使用BatchDue()
:
gron := gronx.New()
exprs := []string{"* * * * *", "0 */5 * * * *"}
// 返回[]gronx.Expr{}数组,每个项目都有Due标志和遇到的Err
dues := gron.BatchDue(exprs)
for _, expr := range dues {
if expr.Err != nil {
// 处理错误
} else if expr.Due {
// 处理到期任务
}
}
// 或者使用给定时间
ref := time.Now()
gron.BatchDue(exprs, ref)
下一次运行时间
找出cron表达式下一次运行时间(在不久的将来):
allowCurrent = true // 包括当前时间
nextTime, err := gronx.NextTick(expr, allowCurrent) // 返回time.Time, error
// 或者在某个参考时间后的下一次运行
refTime = time.Date(2022, time.November, 1, 1, 1, 0, 0, time.UTC)
allowCurrent = false // 排除参考时间
nextTime, err := gronx.NextTickAfter(expr, refTime, allowCurrent) // 返回time.Time, error
上一次运行时间
找出cron表达式上一次运行时间(在不久的过去):
allowCurrent = true // 包括当前时间
prevTime, err := gronx.PrevTick(expr, allowCurrent) // 返回time.Time, error
// 或者在某个参考时间前的上一次运行
refTime = time.Date(2022, time.November, 1, 1, 1, 0, 0, time.UTC)
allowCurrent = false // 排除参考时间
nextTime, err := gronx.PrevTickBefore(expr, refTime, allowCurrent) // 返回time.Time, error
Go Tasker
Tasker是一个可以在Golang应用程序中以编程方式使用的任务管理器。它作为守护进程运行,并调用使用cron表达式调度的任务:
package main
import (
"context"
"time"
"github.com/adhocore/gronx/pkg/tasker"
)
func main() {
taskr := tasker.New(tasker.Option{
Verbose: true,
// 可选:默认为本地时区
Tz: "Asia/Bangkok",
// 可选:默认为stderr日志流
Out: "/full/path/to/output-file",
})
// 添加每分钟运行的任务
taskr.Task("* * * * *", func(ctx context.Context) (int, error) {
// 做一些事情...
// 然后返回退出码和错误,例如:如果一切正常
return 0, nil
}).Task("*/5 * * * *", func(ctx context.Context) (int, error) { // 每5分钟
// 你也可以将输出记录到上面Option中配置的Out文件:
taskr.Log.Printf("done something in %d s", 2)
return 0, nil
})
// 运行任务不重叠,将concurrent标志设置为false:
concurrent := false
taskr.Task("* * * * * *", tasker.Taskify("sleep 2", tasker.Option{}), concurrent)
// 每10分钟执行任意命令
taskr.Task("@10minutes", taskr.Taskify("command --option val -- args", tasker.Option{Shell: "/bin/sh -c"}))
// ... 添加更多任务
// 可选地,如果你想让tasker在2小时后停止,使用Until()传递持续时间:
taskr.Until(2 * time.Hour)
// 最后运行tasker,它每分钟精确地触发并运行该时间到期的所有任务!
// 当收到ctrl+c时,它会优雅地退出,确保待处理的任务完成。
taskr.Run()
}
并发性
默认情况下,任务可以并发运行,即如果上一次运行尚未完成但现在又到期了,它将再次运行。如果你想一次只运行一个任务实例,将concurrent标志设置为false:
taskr := tasker.New(tasker.Option{})
concurrent := false
expr, task := "* * * * * *", tasker.Taskify("php -r 'sleep(2);'")
taskr.Task(expr, task, concurrent)
任务守护进程
它也可以作为独立的任务守护进程使用,而不是在Golang应用程序中以编程方式使用。
首先,安装tasker命令:
go install github.com/adhocore/gronx/cmd/tasker@latest
然后准备一个crontab格式的taskfile,最后像这样运行任务守护进程:
tasker -file path/to/taskfile
Tasker命令选项:
-file string <required>
The task file in crontab format
-out string
The fullpath to file where output from tasks are sent to
-shell string
The shell to use for running tasks (default "/usr/bin/bash")
-tz string
The timezone to use for tasks (default "Local")
-until int
The timeout for task daemon in minutes
-verbose
The verbose mode outputs as much as possible
示例:
tasker -verbose -file path/to/taskfile -until 120 # 运行直到120分钟后(即2小时),所有反馈回显
tasker -verbose -file path/to/taskfile -out path/to/output # 所有反馈回显到输出文件
tasker -tz America/New_York -file path/to/taskfile -shell zsh # 使用zsh shell基于纽约时区运行所有任务
Cron表达式
完整的cron表达式由7个部分组成:
<second> <minute> <hour> <day> <month> <weekday> <year>
然而,通常5部分就足够了,5部分表达式解释为:
<minute> <hour> <day> <month> <weekday>
在这种情况下,会在<second>
位置前置一个默认值0。
在6部分表达式中,如果第6部分匹配<year>
(即至少4位数字),它将被解释为:
<minute> <hour> <day> <month> <weekday> <year>
并且会在<second>
位置前置一个默认值0。
对于每个部分,你可以有多个选择,用逗号分隔:
例如:
0 0,30 * * * *
表示第0或第30分钟
要指定值范围,可以使用破折号:
例如:
0 10-15 * * * *
表示第10、11、12、13、14和15分钟
要指定步长范围,可以结合破折号和斜杠:
例如:
0 10-15/2 * * * *
表示10到15分钟之间每2分钟一次,即第10、12和14分钟
对于<day>
和<weekday>
部分,还有额外的修饰符(可选)。
如果你想,你可以在一个表达式中混合多个选择、范围和步长:
0 5,12-20/4,55 * * * *
匹配分钟为5或12-20/4或55中的任意一个
真实缩写
你可以使用真实的缩写(3个字符)表示月份和星期几。例如:JAN
、dec
、fri
、SUN
标签
以下标签可用,它们在解析前会转换为真实的cron表达式:
@yearly
或@annually
- 每年@monthly
- 每月@daily
- 每天@weekly
- 每周@hourly
- 每小时@5minutes
- 每5分钟@10minutes
- 每10分钟@15minutes
- 每15分钟@30minutes
- 每30分钟@always
- 每分钟@everysecond
- 每秒
为了向后兼容,
@always
目前仍表示每分钟,在未来的版本中它可能表示每秒
// 像这样使用标签:
gron.IsDue("@hourly")
gron.IsDue("@5minutes")
修饰符
支持以下修饰符
-
月份中的某天 / 5部分中的第3部分 / 6+部分中的第4部分:
L
表示月份的最后一天(例如:L
在闰年二月可能表示29日)W
表示最近的工作日(例如:10W
是最接近10日的工作日(MON-FRI))
-
星期几 / 5部分中的第5部分 / 6+部分中的第6部分:
L
表示月份的最后一周几(例如:2L
是最后一个星期二)#
表示月份中的第n个星期几(例如:1#2
是第二个星期一)
许可证
© MIT | 2021-2099, Jitendra Adhikari
更多关于golang高效Cron表达式解析与任务调度插件gronx的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang高效Cron表达式解析与任务调度插件gronx的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang高效Cron表达式解析与任务调度插件gronx使用指南
1. 什么是gronx
gronx是一个轻量级、高效的Golang库,用于解析Cron表达式并执行定时任务。相比标准库的cron
包,gronx具有以下优势:
- 更快的解析速度
- 更低的内存占用
- 支持秒级精度
- 更灵活的调度选项
2. 安装gronx
go get github.com/adhocore/gronx
3. 基本使用方法
3.1 简单任务调度
package main
import (
"fmt"
"time"
"github.com/adhocore/gronx"
)
func main() {
gron := gronx.New()
// 定义任务
task := func() {
fmt.Println("任务执行于:", time.Now().Format("2006-01-02 15:04:05"))
}
// 每5秒执行一次
expr := "*/5 * * * * *"
// 启动调度器
gron.AddFunc(expr, task)
gron.Start()
// 运行10分钟后停止
time.Sleep(10 * time.Minute)
gron.Stop()
}
3.2 检查Cron表达式是否有效
package main
import (
"fmt"
"github.com/adhocore/gronx"
)
func main() {
gron := gronx.New()
expressions := []string{
"*/5 * * * * *", // 有效
"* * * * *", // 有效(默认分钟级)
"60 * * * * *", // 无效(秒数超出范围)
}
for _, expr := range expressions {
valid := gron.IsValid(expr)
fmt.Printf("表达式 '%s' 是否有效: %t\n", expr, valid)
}
}
4. 高级功能
4.1 带参数的任务
package main
import (
"fmt"
"time"
"github.com/adhocore/gronx"
)
func main() {
gron := gronx.New()
// 带参数的任务函数
greet := func(name string) {
fmt.Printf("Hello, %s! 现在时间是: %s\n",
name, time.Now().Format("15:04:05"))
}
// 每10秒执行一次
gron.AddFunc("*/10 * * * * *", func() {
greet("Gopher")
})
gron.Start()
defer gron.Stop()
// 运行5分钟
time.Sleep(5 * time.Minute)
}
4.2 一次性任务
package main
import (
"fmt"
"time"
"github.com/adhocore/gronx"
)
func main() {
gron := gronx.New()
// 在下一分钟的0秒执行
nextMinute := time.Now().Truncate(time.Minute).Add(time.Minute)
expr := fmt.Sprintf("%d * * * * *", nextMinute.Second())
gron.AddFunc(expr, func() {
fmt.Println("一次性任务执行于:", time.Now().Format("2006-01-02 15:04:05"))
})
gron.Start()
defer gron.Stop()
// 等待足够时间让任务执行
time.Sleep(2 * time.Minute)
}
4.3 任务标签管理
package main
import (
"fmt"
"time"
"github.com/adhocore/gronx"
)
func main() {
gron := gronx.New()
// 添加带标签的任务
gron.AddTaggedFunc("job1", "*/15 * * * * *", func() {
fmt.Println("Job1执行:", time.Now().Format("15:04:05"))
})
gron.AddTaggedFunc("job2", "*/20 * * * * *", func() {
fmt.Println("Job2执行:", time.Now().Format("15:04:05"))
})
gron.Start()
// 5秒后移除job1
time.Sleep(5 * time.Second)
gron.Remove("job1")
// 运行2分钟后停止
time.Sleep(2 * time.Minute)
gron.Stop()
}
5. 性能优化技巧
-
避免在任务函数中执行耗时操作:长时间运行的任务会阻塞后续任务的执行
-
使用Worker Pool:对于可能耗时的任务,可以使用worker pool来并发执行
package main
import (
"fmt"
"sync"
"time"
"github.com/adhocore/gronx"
)
func main() {
gron := gronx.New()
// 创建worker pool
workers := 5
jobs := make(chan func(), 100)
var wg sync.WaitGroup
for i := 0; i < workers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for job := range jobs {
job()
}
}()
}
// 添加任务到worker pool
gron.AddFunc("*/10 * * * * *", func() {
jobs <- func() {
// 模拟耗时任务
time.Sleep(3 * time.Second)
fmt.Println("Worker处理任务:", time.Now().Format("15:04:05"))
}
})
gron.Start()
// 运行10分钟后停止
time.Sleep(10 * time.Minute)
close(jobs)
wg.Wait()
gron.Stop()
}
6. 与标准库cron的对比
特性 | gronx | 标准库cron |
---|---|---|
解析速度 | 更快 | 较慢 |
内存占用 | 更低 | 较高 |
秒级精度 | 支持 | 不支持 |
语法检查 | 有 | 无 |
任务标签管理 | 支持 | 不支持 |
7. 总结
gronx是一个高效、灵活的Golang定时任务调度库,特别适合需要高性能和精细控制的场景。通过本文的介绍,您应该已经掌握了gronx的基本用法和高级特性,可以在实际项目中根据需求选择合适的调度方案。
对于大多数应用场景,gronx都能提供出色的性能表现,特别是当您需要秒级精度或大量定时任务时,gronx的优势会更加明显。