crontab
定时任务
golang
package main
import(
  "fmt"

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

func main() {
    crontab := cron.New()
    task := func() {
        fmt.Println("hello world")
    }
  // 添加定时任务, * * * * * 是 crontab,表示每分钟执行一次
    crontab.AddFunc("* * * * *", task)
  // 启动定时器
    crontab.Start()
  // 定时任务是另起协程执行的,这里使用 select 简答阻塞.实际开发中需要
  // 根据实际情况进行控制
    select {}
}

注:

cron.WithPanicLogger

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

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

话不多说,直接贴代码:

package main

import (
    "fmt"
    "sync"

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

// Crontab crontab manager
type Crontab struct {
    inner *cron.Cron
    ids   map[string]cron.EntryID
    mutex sync.Mutex
}

// NewCrontab new crontab
func NewCrontab() *Crontab {
    return &Crontab{
        inner: cron.New(),
        ids:   make(map[string]cron.EntryID),
    }
}

// IDs ...
func (c *Crontab) IDs() []string {
    c.mutex.Lock()
    defer c.mutex.Unlock()
    validIDs := make([]string, 0, len(c.ids))
    invalidIDs := make([]string, 0)
    for sid, eid := range c.ids {
        if e := c.inner.Entry(eid); e.ID != eid {
            invalidIDs = append(invalidIDs, sid)
            continue
        }
        validIDs = append(validIDs, sid)
    }
    for _, id := range invalidIDs {
        delete(c.ids, id)
    }
    return validIDs
}

// Start start the crontab engine
func (c *Crontab) Start() {
    c.inner.Start()
}

// Stop stop the crontab engine
func (c *Crontab) Stop() {
    c.inner.Stop()
}

// DelByID remove one crontab task
func (c *Crontab) DelByID(id string) {
    c.mutex.Lock()
    defer c.mutex.Unlock()

    eid, ok := c.ids[id]
    if !ok {
        return
    }
    c.inner.Remove(eid)
    delete(c.ids, id)
}

// AddByID add one crontab task
// id is unique
// spec is the crontab expression
func (c *Crontab) AddByID(id string, spec string, cmd cron.Job) error {
    c.mutex.Lock()
    defer c.mutex.Unlock()

    if _, ok := c.ids[id]; ok {
        return errors.Errorf("crontab id exists")
    }
    eid, err := c.inner.AddJob(spec, cmd)
    if err != nil {
        return err
    }
    c.ids[id] = eid
    return nil
}

// AddByFunc add function as crontab task
func (c *Crontab) AddByFunc(id string, spec string, f func()) error {
    c.mutex.Lock()
    defer c.mutex.Unlock()

    if _, ok := c.ids[id]; ok {
        return errors.Errorf("crontab id exists")
    }
    eid, err := c.inner.AddFunc(spec, f)
    if err != nil {
        return err
    }
    c.ids[id] = eid
    return nil
}

// IsExists check the crontab task whether existed with job id
func (c *Crontab) IsExists(jid string) bool {
    _, exist := c.ids[jid]
    return exist
}

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

type testTask struct {
}

func (t *testTask) Run() {
    fmt.Println("hello world")
}

func main() {
    crontab := NewCrontab()
    // 实现接口的方式添加定时任务
    task := &testTask{}
    if err := crontab.AddByID("1", "* * * * *", task); err != nil {
        fmt.Printf("error to add crontab task:%s", err)
        os.Exit(-1)
    }

    // 添加函数作为定时任务
    taskFunc := func() {
        fmt.Println("hello world")
    }
    if err := crontab.AddByFunc("2", "* * * * *", taskFunc); err != nil {
        fmt.Printf("error to add crontab task:%s", err)
        os.Exit(-1)
    }
    crontab.Start()
    select {}

}

注:

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

不足:

withChaincron.Recover
后记

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