
相信大家都或多或少了解过这个定时任务库github.com/robfig/cron/v3,在使用的时候,有一类写法是这样的
_, err := tasksCron.AddFunc("@every 10s", xxx)看到这样的写法,一方面觉得确实方便,另一方面也对它背后的实现机制产生了好奇:它是如何理解并解析这样自然的语言式描述的呢?今天我来深入一下源码,探究其实现原理。
在深入细节之前,我们先了解一下 AddFunc方法处理一个描述符(如 "@every 10s") 的大致流程:
接收字符串:方法接收到用户传入的 cron 表达式或描述符字符串。
解析器选择:库需要判断这个字符串是传统的 cron 表达式(如 "0 * * * *"),还是它们支持的更人性化的描述符(如 "@every 10s"或 "@daily")。
描述符解析:如果判断是描述符,则进入特定的解析分支,将其转换为一个 Schedule接口对象,该对象定义了下次执行时间该如何计算。
返回调度器:解析成功后,返回这个 Schedule对象,后续的定时触发就由这个对象来控制。
首先,它是先定义一批比较常规且时间间隔比较长的描述符
switch descriptor {
case "@yearly", "@annually":
// 每年执行一次,等同于 "0 0 1 1 *"
case "@monthly":
// 每月执行一次,等同于 "0 0 1 * *"
case "@weekly":
// 每周执行一次,等同于 "0 0 * * 0"
case "@daily", "@midnight":
// 每天执行一次,等同于 "0 0 * * *"
case "@hourly":
// 每小时执行一次,等同于 "0 * * * *"
}这些预定义描述符为开发者提供了便利,无需记忆复杂的 Cron 表达式, 就可实现常见的时间调度需求。
我比较好奇的是它是如何解析自定义的间隔的,我之前设想的是用正则表达式提取数字和后面的时间单位,然后进行处理。
但实际上它是这样实现的
const every = "@every "
if strings.HasPrefix(descriptor, every) {
duration, err := time.ParseDuration(descriptor[len(every):])
if err != nil {
return nil, fmt.Errorf("failed to parse duration %s: %s", descriptor, err)
}
return Every(duration), nil
}极简的字符串处理:没有引入复杂的正则表达式,仅仅使用 strings.HasPrefix和字符串切片,效率极高。
重用标准库:直接利用了 Go 语言内置的 time.ParseDuration函数。这个函数本身已经非常强大,支持诸如 "300ms", "1.5h", "2h45m"等各种复杂组合,使得 @every描述符的能力直接与其对齐,支持所有标准时间单位(ns, us, ms, s, m, h)。
健壮性:标准库的解析函数经过了充分测试,其稳定性和正确性远胜于自己编写的正则表达式,极大地减少了潜在的 bug。
可读性与可维护性:代码清晰易懂,后续维护成本低。
通过对 github.com/robfig/cron/v3库描述符解析部分的源码分析,可以看到其设计上的一些亮点:
性化接口:通过 @yearly、@every等描述符,提供了比传统 cron 表达式更直观的定时配置方式。
简洁的实现:没有过度设计,利用语言特性和标准库,用最直接的字符串操作和标准解析函数完成了核心功能。
强大的扩展性:得益于 time.ParseDuration的强大,@every描述符天然支持所有时间单位和组合,挺巧妙。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。