golang定时任务之cron

简介

github.com/robfig/cron

服务器应用中,定时任务是常见的业务需求。一些清理动作、统计、重试等耗时长、占用资源多的应用,期望在业务低峰期定时执行。通常根据需求,分为每天/每周/每月执行。cron是golang中广泛使用的一个开源项目

demo try

go 复制代码
package main

import (
	"fmt"
	"time"

	"github.com/robfig/cron/v3"
)

func main() {
	c := cron.New()
	c.AddFunc("@every 5s", func() {
		fmt.Println(time.Now(), "run every 5 second")
	})
	c.AddFunc("4 * * * ?", func() {
		fmt.Println(time.Now(), "run minute 4 every hour")
	})
	c.Start()
	select {}
}

输出:

ini 复制代码
2024-01-23 22:03:30.001179 +0800 CST m=+4.163984251 run every 5 second
2024-01-23 22:03:35.000493 +0800 CST m=+9.163356209 run every 5 second
2024-01-23 22:03:40.001159 +0800 CST m=+14.164079876 run every 5 second
2024-01-23 22:03:45.001147 +0800 CST m=+19.164125626 run every 5 second
2024-01-23 22:03:50.001245 +0800 CST m=+24.164280209 run every 5 second
2024-01-23 22:03:55.001376 +0800 CST m=+29.164468959 run every 5 second
2024-01-23 22:04:00.004014 +0800 CST m=+34.167164418 run minute 4 every hour
2024-01-23 22:04:00.004155 +0800 CST m=+34.167305751 run every 5 second
2024-01-23 22:04:05.001184 +0800 CST m=+39.164391918 run every 5 second

一探究竟

类图

cron

Cron是主类,其中主要属性:
  • entries记录了任务列表,包含了任务的id、信息等

  • stop、add、remove,通过chan方式,传递job信息的添加、删除操作和cron的stop

  • Chain,装饰者模式,为cron的job提供多个jobWrapper,可以实现log、同步等功能

  • running记录当前cron的运行状态

  • runningMu,运行锁,在添加、删除、启动、停止时,均需获取runningMu锁

  • parser为cron表达式的格式,默认以"Minute | Hour | Dom | Month | Dow | Descriptor"的格式,详情可参考en.wikipedia.org/wiki/Cron

  • nextId记录当前最大的jobId

    Cron提供的主要方法:
  • 添加任务:可以通过AddFunc、AddJob直接添加待执行任务,或者通过自定义spec parse初始化schedule,通过Schedule func添加任务

  • 删除任务:Remove

  • Start: 启动全部定时任务,cron的启动需要显示启动

  • Stop: 停止cron。会等待所有正在执行的任务完毕stop

    定时任务的调度机制

    当调用Cron.Start()方法后,会调用Cron.run()

  • 首先计算每个任务的下一个运行时间点

    go 复制代码
    	// Figure out the next activation times for each entry.
    	now := c.now()
    	for _, entry := range c.entries {
    		entry.Next = entry.Schedule.Next(now)
    		c.logger.Info("schedule", "now", now, "entry", entry.ID, "next", entry.Next)
    	}
  • 然后进入循环任务,在循环任务中,首先根据任务的下次执行时间,进行升序排列

    less 复制代码
    sort.Sort(byTime(c.entries))
  • 设置定时器,下一个任务的执行时间开始提醒

  • 检测增、删、停等channel是否有信号,进行任务管理操作

    vbnet 复制代码
    	case newEntry := <-c.add:
    		timer.Stop()
    		now = c.now()
    		newEntry.Next = newEntry.Schedule.Next(now)
    		c.entries = append(c.entries, newEntry)
    		c.logger.Info("added", "now", now, "entry", newEntry.ID, "next", newEntry.Next)
    
    	case replyChan := <-c.snapshot:
    		replyChan <- c.entrySnapshot()
    		continue
    
    	case <-c.stop:
    		timer.Stop()
    		c.logger.Info("stop")
    		return
    
    	case id := <-c.remove:
    		timer.Stop()
    		now = c.now()
    		c.removeEntry(id)
    		c.logger.Info("removed", "entry", id)
  • 当到达下个任务的执行时间,执行该任务,设置该任务的下次执行时间

    vbnet 复制代码
    	case now = <-timer.C:
    		now = now.In(c.location)
    		c.logger.Info("wake", "now", now)
    
    		// Run every entry whose next time was less than now
    		for _, e := range c.entries {
    			if e.Next.After(now) || e.Next.IsZero() {
    				break
    			}
    			c.startJob(e.WrappedJob)
    			e.Prev = e.Next
    			e.Next = e.Schedule.Next(now)
    			c.logger.Info("run", "now", now, "entry", e.ID, "next", e.Next)
    		}
  • 执行任务时,新建goroutine的方式来执行该任务,故若同一个任务,不同批次的执行不会相互影响

    scss 复制代码
    	c.jobWaiter.Add(1)
    	go func() {
    		defer c.jobWaiter.Done()
    		j.Run()
    	}()

    配置项

    cron提供了一些常用配置项,供开发者自行设置

  • 时区信息: WithLocation(loc *time.Location)

  • 支持秒级任务定时: WithSeconds()

  • 支持自定义cron表达式格式: WithParser(p Parse) Option

  • 支持自定义Job的封装方法,可以自定义日志、同步机制、甚至引入prometheus监控等: WithChain(wrappers ...JobWrapper)

  • 支持自定义Logger: WithLogger(logger Logger)

    总结

    整体来说,cron通过简单清晰的任务调度机制,实现了定时任务的执行。同时,也提供了丰富的定制化功能。使用者可以根据自身需要进行扩展。

相关推荐
测试19982 小时前
2024软件测试面试热点问题
自动化测试·软件测试·python·测试工具·面试·职场和发展·压力测试
马剑威(威哥爱编程)3 小时前
MongoDB面试专题33道解析
数据库·mongodb·面试
独行soc5 小时前
#渗透测试#SRC漏洞挖掘#深入挖掘XSS漏洞02之测试流程
web安全·面试·渗透测试·xss·漏洞挖掘·1024程序员节
理想不理想v5 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试
童先生5 小时前
Go 项目中实现类似 Java Shiro 的权限控制中间件?
开发语言·go
sszmvb12346 小时前
测试开发 | 电商业务性能测试: Jmeter 参数化功能实现注册登录的数据驱动
jmeter·面试·职场和发展
测试杂货铺6 小时前
外包干了2年,快要废了。。
自动化测试·软件测试·python·功能测试·测试工具·面试·职场和发展
王佑辉6 小时前
【redis】redis缓存和数据库保证一致性的方案
redis·面试
真忒修斯之船6 小时前
大模型分布式训练并行技术(三)流水线并行
面试·llm·aigc
ZL不懂前端7 小时前
Content Security Policy (CSP)
前端·javascript·面试