Java架构师成长直通车(40周完结无密内附文档源码)

2年前 (2022) 程序员胖胖胖虎阿
181 0 0

Java架构师成长直通车(40周完结无密内附文档源码)

下载地址:百度网盘
解析 Golang 定时任务库 gron 设计和原理
简单说,每一个位都代表了一个时间维度,* 代表全集,所以,上面的语义是:在每天早上的4点05分触发任务。
但 cron 毕竟只是一个操作系统级别的工具,假如定时任务失败了,或者压根没启动,cron 是没法提示开发者这一点的。并且,cron 和 正则表达式都有一种魔力,不晓得大家能否感同身受,这里援用同事的一句名言:

这世界上有些言语十分类似: shell脚本, es查询的那个dsl言语, 定时任务的crontab, 正则表达式. 他们类似就类似在每次要写的时分根本都得重新现学一遍。

正巧,最近看到了 gron 这个开源项目,它是用 Golang 完成一个并发平安的定时任务库。完成十分简单精巧,代码量也不多。今天我们就来一同分离源码看一下,怎样基于 Golang 的才能做出来一个【定时任务库】。

Gron provides a clear syntax for writing and deploying cron jobs.

gron 是一个泰国小哥在 2016 年开源的作品,它的特性就在于十分简单和明晰的语义来定义【定时任务】,你不用再去记 cron 的语法。我们来看下作为运用者怎样上手。
首先,我们还是一个 go get 装置依赖:
$ go get github.com/roylee0704/gron
复制代码
假定我们希冀在【机遇】到了以后,要做的工作是打印一个字符串,每一个小时执行一次,我们就能够这样:
package main

import (

"fmt"
"time"
"github.com/roylee0704/gron"

)

func main() {

c := gron.New()
c.AddFunc(gron.Every(1*time.Hour), func() {
    fmt.Println("runs every hour.")
})
c.Start()

}
复制代码
十分简单,而且即使是在 c.Start 之后我们仍然能够添加新的定时任务进去。支持了很好的扩展性。
定时参数
留意到我们调用 gron.New().AddFunc() 时传入了一个 gron.Every(1*time.Hour)。
这里其实你能够传入任何一个 time.Duration,从而把调度距离从 1 小时调整到 1 分钟以至 1 秒。
除此之外,gron 还很贴心肠封装了一个 xtime 包用来把常见的 time.Duration 封装起来,这里我们开箱即用。
import "github.com/roylee0704/gron/xtime"

gron.Every(1 * xtime.Day)
gron.Every(1 * xtime.Week)
复制代码
很多时分我们不只仅某个任务在当天运转,还希望是我们指定的时辰,而不是依赖程序启动时间,机械地加 24 hour。gron 对此也做了很好的支持:
gron.Every(30 * xtime.Day).At("00:00")
gron.Every(1 * xtime.Week).At("23:59")
复制代码
我们只需指定 At("hh:mm") 就能够完成在指定时间执行。
源码解析
这一节我们来看看 gron 的完成原理。
所谓定时任务,其实包含两个层面:

触发器。即我们希望这个任务在什么时间点,什么周期被触发;
任务。即我们在触发之后,希望执行的任务,类比到我们上面示例的 fmt.Println。

对这两个概念的封装和扩展是一个定时任务库必需思索的。
而同时,我们是在 Golang 的协程上跑程序的,意味着这会是一个长期运转的协程,否则你即使指定了【一个月后干XXX】这个任务,程序两天后挂了,也就无法完成你的诉求了。
所以,我们还希望有一个 manager 的角色,来管理我们的一组【定时任务】,如何调度,什么时分启动,怎样中止,启动了以后还想加新任务能否支持。
Cron
在 gron 的体系里,Cron 对象(我们上面经过 gron.New 创立出来的)就是我们的 manager,而底层的一个个【定时任务】则对应到 Cron 对象中的一个个 Entry:
// Cron provides a convenient interface for scheduling job such as to clean-up
// database entry every month.
//
// Cron keeps track of any number of entries, invoking the associated func as
// specified by the schedule. It may also be started, stopped and the entries
// may be inspected.
type Cron struct {

entries []*Entry
running bool
add     chan *Entry
stop    chan struct{}

}

// New instantiates new Cron instant c.
func New() *Cron {

return &Cron{
    stop: make(chan struct{}),
    add:  make(chan *Entry),
}

}
复制代码

entries 就是定时任务的中心才能,它记载了一组【定时任务】;
running 用来标识这个 Cron 能否曾经启动;
add 是一个channel,用来支持在 Cron 启动后,新增的【定时任务】;
stop 同样是个channel,留意到是空构造体,用来控制 Cron 的中止。这个其实是经典写法了,对日常开发也有自创意义,我们待会儿会好美观一下。

我们察看到,当调用 gron.New() 办法后,得到的是一个指向 Cron 对象的指针。此时只是初始化了 stop 和 add 两个 channel,没有启动调度。
Entry
重头戏来了,Cron 里面的 []*Entry 其实就代表了一组【定时任务】,每个【定时任务】能够简化了解为 <触发器,任务> 组成的一个 tuple。
// Entry consists of a schedule and the job to be executed on that schedule.
type Entry struct {

Schedule Schedule
Job      Job

// the next time the job will run. This is zero time if Cron has not been
// started or invalid schedule.
Next time.Time

// the last time the job was run. This is zero time if the job has not been
// run.
Prev time.Time

}

// Schedule is the interface that wraps the basic Next method.
//
// Next deduces next occurring time based on t and underlying states.
type Schedule interface {

Next(t time.Time) time.Time

}

// Job is the interface that wraps the basic Run method.
//
// Run executes the underlying func.
type Job interface {

Run()

}
复制代码

Schedule 代表了一个【触发器】,或者说一个定时战略。它只包含一个 Next 办法,承受一个时间点,业务要返回下一次触发调动的时间点。
Job 则是对【任务】的笼统,只需求完成一个 Run 办法,没有入参出参。

除了这两个中心依赖外,Entry 构造还包含了【前一次执行时间点】和【下一次执行时间点】,这个目前能够疏忽,只是为了辅助代码用。
依照时间排序
// byTime is a handy wrapper to chronologically sort entries.
type byTime []*Entry

func (b byTime) Len() int { return len(b) }
func (b byTime) Swap(i, j int) { b[i], b[j] = b[j], b[i] }

// Less reports earliest time i should sort before j.
// zero time is not earliest time.
func (b byTime) Less(i, j int) bool {

if b[i].Next.IsZero() {
    return false
}
if b[j].Next.IsZero() {
    return true
}

return b[i].Next.Before(b[j].Next)

}
复制代码
这里是对 Entry 列表的简单封装,由于我们可能同时有多个 Entry 需求调度,处置的次第很重要。这里完成了 sort 的接口, 有了 Len(), Swap(), Less() 我们就能够用 sort.Sort() 来排序了。
此处的排序战略是依照时间大小。

相关文章

暂无评论

暂无评论...