robfig/cron-go cron定时任务库架构剖析
创始人
2024-03-07 23:34:21
0

Cron深度解析


思想

对于cron 这个三方库来说,他可以说是做两件事,其一是:解析cron string,生成一个定时器,达到循环时间发送信号。其二是核心(引擎):用以执行,判断,接收任务,停止cron等事情。

核心思想是面向接口,抽象开发。只有抽象才能使得哪怕九年时间过去,依然是这么简洁,普及。因为脱离了现实世界到达了柏拉图心中的理念世界理想国。

而为什么接口的实现是只要继承接口方法而不是保持对象信息呢?柏拉图的理想国认为现实世界的一切都是理念世界中各自本体的投影。那本体有哪些特征可以被用来在现实世界中与对象做判断呢?这里就涉及到唯心唯物了。主观唯心认为我与世界交互所以有了世界,如王阳明,笛卡尔,康德,佛教教义。把他们划到主观唯心也是不太对的,因为或许这就是真相,但却用两个带有偏见的名词对其做概括。之所以说主观唯心,一句诗概括就是:你未看此花时,此花与汝心同归于寂:你来看此花时,则此花颜色一时明白起来:便知此花不在你的心外。就像判断结构体是否实现接口就看是否实现了动作!因为只有动词 表示的是我在与世界做交互,互动所以有了世界。


robfig/cron at v3.0.1架构图

robfig/cron 架构图


关键位置分析

Cron 实现 Executable 接口

Executable 接口 有三个方法,Cron结构体继承。

  • 通过Start 会 启动一个并行队列
  • 通过Run 会启动一个串行队列
// Start the cron scheduler in its own go-routine, or no-op if already started.
func (c *Cron) Start() {if c.running {return}c.running = truego c.run()
}// Run the cron scheduler, or no-op if already running.
func (c *Cron) Run() {if c.running {return}c.running = truec.run()
}

Cron 结构体分析

// Cron keeps track of any number of entries, invoking the associated func as
// specified by the schedule. It may be started, stopped, and the entries may
// be inspected while running.
type Cron struct {entries  []*Entrystop     chan struct{}add      chan *Entrysnapshot chan []*Entryrunning  boolErrorLog *log.Loggerlocation *time.Location
}
  • entries:指针切片,用以执行当中的entry(即每一个任务)以及接收传进来的任务
  • stop:停止信号
  • add:接收传输管道
  • snapshot:entries 副本,打印需要

cron string 解析

在 AddJob 方法中,通过Parse 函数获取实现了Schedule 接口的结构体:SpecSchedule

func parseDescriptor(descriptor string) (Schedule, error) {switch descriptor {case "@yearly", "@annually":return &SpecSchedule{Second: 1 << seconds.min,Minute: 1 << minutes.min,Hour:   1 << hours.min,Dom:    1 << dom.min,Month:  1 << months.min,Dow:    all(dow),}, nilcase "@monthly":return &SpecSchedule{Second: 1 << seconds.min,Minute: 1 << minutes.min,Hour:   1 << hours.min,Dom:    1 << dom.min,Month:  all(months),Dow:    all(dow),}, nil
......
}// 该结构体实现了 Schedule 接口
type SpecSchedule struct {Second, Minute, Hour, Dom, Month, Dow uint64
}

引擎解析

// Run the scheduler. this is private just due to the need to synchronize
// access to the 'running' state variable.
func (c *Cron) run() {// Figure out the next activation times for each entry.now := c.now()for _, entry := range c.entries {entry.Next = entry.Schedule.Next(now)}for {// Determine the next entry to run.sort.Sort(byTime(c.entries))var timer *time.Timerif len(c.entries) == 0 || c.entries[0].Next.IsZero() {// If there are no entries yet, just sleep - it still handles new entries// and stop requests.timer = time.NewTimer(100000 * time.Hour)} else {timer = time.NewTimer(c.entries[0].Next.Sub(now))}for {select {case now = <-timer.C:now = now.In(c.location)// Run every entry whose next time was less than nowfor _, e := range c.entries {if e.Next.After(now) || e.Next.IsZero() {break}go c.runWithRecovery(e.Job)e.Prev = e.Nexte.Next = e.Schedule.Next(now)}case newEntry := <-c.add:timer.Stop()now = c.now()newEntry.Next = newEntry.Schedule.Next(now)c.entries = append(c.entries, newEntry)case <-c.snapshot:c.snapshot <- c.entrySnapshot()continuecase <-c.stop:timer.Stop()return}break}}
}

就是一个循环,循环等,循环加。

改进点

可以发现在这其中并没有写注销某一个定时任务的方法

但是想实现也是比较简单的,加一个case,监听channel,遍历 entries,找到目标,删除掉就ok了

但是既然它不加,应该是有原因的,不加的原因应该是处于经验考虑的吧?

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
群晖外网访问终极解决方法:IP... 写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法&#x...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
Azure构建流程(Power... 这可能是由于配置错误导致的问题。请检查构建流程任务中的“发布构建制品”步骤,确保正确配置了“Arti...