在实际开发过程中,我们有时候需要编写一些定时任务。当然我们可以使用crontab命令实现我们的需求。但是这种方法不满足一些定制化场景,同时会依赖具体的操作系统环境。

定时任务

golang中我们可以使用cron来实现我们定时任务的需求。他的使用方式非常简单,具体代码如下:

  1. package main
  2. import(
  3. "fmt"
  4. cron "github.com/robfig/cron/v3"
  5. )
  6. func main() {
  7. crontab := cron.New()
  8. task := func() {
  9. fmt.Println("hello world")
  10. }
  11. // 添加定时任务, * * * * * 是 crontab,表示每分钟执行一次
  12. crontab.AddFunc("* * * * *", task)
  13. // 启动定时器
  14. crontab.Start()
  15. // 定时任务是另起协程执行的,这里使用 select 简答阻塞.实际开发中需要
  16. // 根据实际情况进行控制
  17. select {}
  18. }

注:

  • New()函数支持多种初始化选项,比如cron.WithPanicLogger
  • 定时任务是另起协程执行的

上面就是 cron 的最简单使用示例,如果需要了解更加详细的用法,可以参考官方文档和示例。

自定义封装

在上述的使用方法基础上,基于我的实际需求,我对cron库进行了简单封装,主要为实现下面几个需求:

  • 管理所有的定时任务,需要记录定时任务的编号和相关信息
  • 停止一个定时任务
  • 支持添加函数类型和接口类型任务

话不多说,直接贴代码:

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. "github.com/pkg/errors"
  6. cron "github.com/robfig/cron/v3"
  7. )
  8. // Crontab crontab manager
  9. type Crontab struct {
  10. inner *cron.Cron
  11. ids map[string]cron.EntryID
  12. mutex sync.Mutex
  13. }
  14. // NewCrontab new crontab
  15. func NewCrontab() *Crontab {
  16. return &Crontab{
  17. inner: cron.New(),
  18. ids: make(map[string]cron.EntryID),
  19. }
  20. }
  21. // IDs ...
  22. func (c *Crontab) IDs() []string {
  23. c.mutex.Lock()
  24. defer c.mutex.Unlock()
  25. validIDs := make([]string, 0, len(c.ids))
  26. invalidIDs := make([]string, 0)
  27. for sid, eid := range c.ids {
  28. if e := c.inner.Entry(eid); e.ID != eid {
  29. invalidIDs = append(invalidIDs, sid)
  30. continue
  31. }
  32. validIDs = append(validIDs, sid)
  33. }
  34. for _, id := range invalidIDs {
  35. delete(c.ids, id)
  36. }
  37. return validIDs
  38. }
  39. // Start start the crontab engine
  40. func (c *Crontab) Start() {
  41. c.inner.Start()
  42. }
  43. // Stop stop the crontab engine
  44. func (c *Crontab) Stop() {
  45. c.inner.Stop()
  46. }
  47. // DelByID remove one crontab task
  48. func (c *Crontab) DelByID(id string) {
  49. c.mutex.Lock()
  50. defer c.mutex.Unlock()
  51. eid, ok := c.ids[id]
  52. if !ok {
  53. return
  54. }
  55. c.inner.Remove(eid)
  56. delete(c.ids, id)
  57. }
  58. // AddByID add one crontab task
  59. // id is unique
  60. // spec is the crontab expression
  61. func (c *Crontab) AddByID(id string, spec string, cmd cron.Job) error {
  62. c.mutex.Lock()
  63. defer c.mutex.Unlock()
  64. if _, ok := c.ids[id]; ok {
  65. return errors.Errorf("crontab id exists")
  66. }
  67. eid, err := c.inner.AddJob(spec, cmd)
  68. if err != nil {
  69. return err
  70. }
  71. c.ids[id] = eid
  72. return nil
  73. }
  74. // AddByFunc add function as crontab task
  75. func (c *Crontab) AddByFunc(id string, spec string, f func()) error {
  76. c.mutex.Lock()
  77. defer c.mutex.Unlock()
  78. if _, ok := c.ids[id]; ok {
  79. return errors.Errorf("crontab id exists")
  80. }
  81. eid, err := c.inner.AddFunc(spec, f)
  82. if err != nil {
  83. return err
  84. }
  85. c.ids[id] = eid
  86. return nil
  87. }
  88. // IsExists check the crontab task whether existed with job id
  89. func (c *Crontab) IsExists(jid string) bool {
  90. _, exist := c.ids[jid]
  91. return exist
  92. }

代码实现很简单,每个函数的作用都可以参考注释.下面简单实用一下上面的封装:

  1. type testTask struct {
  2. }
  3. func (t *testTask) Run() {
  4. fmt.Println("hello world")
  5. }
  6. func main() {
  7. crontab := NewCrontab()
  8. // 实现接口的方式添加定时任务
  9. task := &testTask{}
  10. if err := crontab.AddByID("1", "* * * * *", task); err != nil {
  11. fmt.Printf("error to add crontab task:%s", err)
  12. os.Exit(-1)
  13. }
  14. // 添加函数作为定时任务
  15. taskFunc := func() {
  16. fmt.Println("hello world")
  17. }
  18. if err := crontab.AddByFunc("2", "* * * * *", taskFunc); err != nil {
  19. fmt.Printf("error to add crontab task:%s", err)
  20. os.Exit(-1)
  21. }
  22. crontab.Start()
  23. select {}
  24. }

注:

  • task id 是唯一的,实际开发可以使用 uuid
  • 这个封装是并发安全的

不足:

  • 未支持初始化参数
  • 定时任务的错误采集,如果某个定时任务出错,应该能够获取到错误信息(这里指的是错误不是 panic)
  • panic 恢复操作可以参考 withChaincron.Recover

后记

下一篇详细解析 cron 的实现原理和更加复杂的用法

golang 实现定时任务的更多相关文章

  1. golang的定时任务

    golang的定时任务使用的是cron这个包来解决的 官方文档地址:https://godoc.org/github.com/robfig/cron cron包的基础知识 字段名 是否必须 允许的值 ...

  2. golang cronexpr定时任务包使用

    包获取 go get -u github.com/gorhill/cronexpr 创建一个定时任务 expr, err = cron.Parse("* * * * *"); 获得 ...

  3. Golang cron 定时任务使用

    1.cron 表达式的基本格式 用过 linux 的应该对 cron 有所了解.linux 中可以通过 crontab -e 来配置定时任务.不过,linux 中的 cron 只能精确到分钟.而我们这 ...

  4. Golang——Cron 定时任务

    开门见山写一个 package main import ( "fmt" "github.com/robfig/cron" "log" &qu ...

  5. cron表达式的双重人格:星期和数字到底如何对应?

    写在前面 cron在希腊语中是时间的意思,而cron表达式(cron expression)则是遵循特定规则,用于描述定时设置的字符串,常用于执行定时任务.本文总结了不同环境(如平台.库等)下,cro ...

  6. Golang 入门系列(八) cron定时任务

    1.cron 表达式的基本格式  Go 实现的cron 表达式的基本语法跟linux 中的 crontab基本是类似的.cron(计划任务),就是按照约定的时间,定时的执行特定的任务(job).cro ...

  7. Golang定时任务简单实现

    下载cron包: go get github.com/robfig/cron 开启一个定时: 根据cron表达式进行时间调度,cron可以精确到秒,大部分表达式格式也是从秒开始. c := cron. ...

  8. Go 定时任务

    本文基于Golang Crontab 实现了一个Crontab Job Manager.更加容易使用,同时也能够满足更加复杂的场景. 仓储地址, 如果有用,欢迎点赞,欢迎讨论,欢迎找茬. 需求 在开发 ...

  9. Golang mysql 上线的一个坑 Db.close重要性

    急冲冲完成的mysql的一个监控自动处理程序上线了,线下处理是正常的,没想到线上才半小时就奔溃了. 现在时间是晚上11点,心慌焦虑涌上心头,需要熬夜?肾上腺素激增. 程序主要是一个定时任务的处理程序, ...

随机推荐

  1. Shell的语法

    Shell的语法: 变量:字符串.数字.环境和参数: 条件:shell中的布尔值: 程序控制:if.elif.for.while.until.case: 命令列表: 函数: Shell内置命令: 获取 ...

  2. mysql 修改表名

    //重命名表 rename table table1 to table2; //重命名多个表 rename table table1 to table2,table3 to table4,table5 ...

  3. 备份和还原 Linux 上的 SQL Server 数据库

    备份数据库 在下面的示例sqlcmd连接到本地 SQL Server 实例,并采用完整备份的名为的用户数据库demodb. sqlcmd -S localhost -U SA -Q "BAC ...

  4. 【算法编程 C++ Python】字符串替换

    题目描述 请实现一个函数,将一个字符串中的空格替换成“%20”.例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy.   C++使用string,pyt ...

  5. Hadoop综合大作业1

    本次作业来源于:https://edu.cnblogs.com/campus/gzcc/GZCC-16SE1/homework/3363 一.课程评分标准: 分数组成: 考勤 10 平时作业 30 爬 ...

  6. github上如何删除一个项目(仓库)

    备忘  链接:https://blog.csdn.net/deng0zhaotai/article/details/38535251

  7. Python3之logging模块浅析

    Python3之logging模块浅析   目录 Python3之logging模块浅析 简单用法 日志与控制台同时输出 一个同时输出到屏幕.文件的完成例子 日志文件截取 日志重复打印问题解决 问题分 ...

  8. 接口测试中模拟post四种请求数据

    https://www.jianshu.com/p/3b6d7aa2043a 一.背景介绍 在日常的接口测试工作中,模拟接口请求通常有两种方法,fiddler模拟和HttpClient模拟. Fidd ...

  9. packaged_task

    /** @file packaged_task.cpp * @note * @brief * @author * @date 2019-8-15 * @note * @history * @warni ...

  10. WARNING:Your password has expired --linux 用户密码过期

    今天在ssh 提示  WARNING:Your password has expired 设置用户到期时间 chage -M 36000 用户名 chage -l 用户名 #查看用户信息