Gin Practice Serial Eleven Cron Timed Tasks

  crontab, golang

Cron timed task

Project address:https://github.com/EDDYCJY/go …

If it is helpful to you, please click Star or Praise.

In practical application projects, the use of timed tasks is very common. Have you ever had any questions about how Golang does the timing task, is it polling?

In this article, we will talk about Cron in connection with our project.

Introduction

We will usecronThis package, which implements the cron specification parser and the task runner, simply includes the functions required for scheduled tasks

Cron expression format

field name Is it required Allowable values Special characters allowed
Seconds Yes 0-59 * / , –
Minutes Yes 0-59 * / , –
Hours Yes 0-23 * / , –
Day of month Yes 1-31 * / , – ?
Month Yes 1-12 or JAN-DEC * / , –
Day of week Yes 0-6 or SUN-SAT * / , – ?

Cron expression represents a set of time, fields separated by 6 spaces

It can be noticed that Golang’s Cron is one second more than Crontab, and it will be easier to meet the second requirement later.

Cron special character

1. asterisks (*)

An asterisk indicates that all values of the field will be matched.

2, slash (/)

The increment of slash user description range is expressed as “N-MAX/x” and the form of first-last/x, for example, 3-59/15 means the third minute at this time and every 15 minutes thereafter until 59 minutes. I.e. starting from n, increment is used until the end of this specific range. It will not repeat

3. Comma (,)

Commas are used to separate items in the list. For example, using “MON, wet, FRI” in Day of week will mean Monday, Wednesday and Friday

4. hyphen (-)

Hyphens are used to define ranges. For example, 9-17 means every hour from 9 a.m. to 5 p.m.

5. Question mark (? )

No value is specified to replace “*”, which is similar to “_”, so it is not difficult to understand.

Predefined Cron schedule

Input sketch Equivalent to
@yearly (or @annually) Run once at midnight on January 1 0 0 0 1 1 *
@monthly At midnight of each month, it runs once in the first month of each month. 0 0 0 1
@weekly It runs once a week and at midnight on Sunday. 0 0 0 0
@daily (or @midnight) Run once a day at midnight. 0 0 0 *
@hourly Run every hour 0 0

Installation

$ go get -u github.com/robfig/cron

Practice

In the previous chapterGin Practice Serial Ten Customized GORM CallbacksIn, we used GORM’s callback to implement soft deletion, while introducing another problem

Is how I hard delete, when I hard delete? This is often related to business scenarios, roughly

  • There is also a hard delete interface
  • Scheduled tasks clean up (or transfer, backup) invalid data

Here we choose the second solution to practice

Write hard delete code

Open the tag.go and article.go files in the models directory and add the following codes respectively

1、tag.go

func CleanAllTag() bool {
    db.Unscoped().Where("deleted_on != ? ", 0).Delete(&Tag{})

    return true
}

2、article.go

func CleanAllArticle() bool {
    db.Unscoped().Where("deleted_on != ? ", 0).Delete(&Article{})

    return true
}

Pay attention to hard delete to useUnscoped()This is GORM’s agreement

Write Cron

Create a new cron.go file under the root directory of the project, which is used to write the code for scheduled tasks and write the contents of the file.

package main

import (
    "time"
    "log"

    "github.com/robfig/cron"

    "github.com/EDDYCJY/go-gin-example/models"
)

func main() {
    log.Println("Starting...")

    c := cron.New()
    c.AddFunc("* * * * * *", func() {
        log.Println("Run models.CleanAllTag...")
        models.CleanAllTag()
    })
    c.AddFunc("* * * * * *", func() {
        log.Println("Run models.CleanAllArticle...")
        models.CleanAllArticle()
    })

    c.Start()

    t1 := time.NewTimer(time.Second * 10)
    for {
        select {
        case <-t1.C:
            t1.Reset(time.Second * 10)
        }
    }
}

In this procedure, we did the following things

1、cron.New()

A new (blank) Cron job runner is created based on local time

func New() *Cron {
    return NewWithLocation(time.Now().Location())
}

// NewWithLocation returns a new Cron job runner.
func NewWithLocation(location *time.Location) *Cron {
    return &Cron{
        entries:  nil,
        add:      make(chan *Entry),
        stop:     make(chan struct{}),
        snapshot: make(chan []*Entry),
        running:  false,
        ErrorLog: nil,
        location: location,
    }
}

2、c.AddFunc()

AddFunc adds a func to Cron job runner to run according to the given schedule.

func (c *Cron) AddJob(spec string, cmd Job) error {
    schedule, err := Parse(spec)
    if err != nil {
        return err
    }
    c.Schedule(schedule, cmd)
    return nil
}

The Schedule will be analyzed first. If there is any problem, err will be directly applied. If there is no problem, func will be added to the schedule queue to wait for execution.

func (c *Cron) Schedule(schedule Schedule, cmd Job) {
    entry := &Entry{
        Schedule: schedule,
        Job:      cmd,
    }
    if !c.running {
        c.entries = append(c.entries, entry)
        return
    }

    c.add <- entry
}

3、c.Start()

Start the Cron scheduler in the currently executing program. In fact, the main body here is the scheduling control of goroutine+for+select+timer

func (c *Cron) Run() {
    if c.running {
        return
    }
    c.running = true
    c.run()
}

4、time.NewTimer + for + select + t1.Reset

If you are a beginner, you will probably have questions. What is this for?

(1)time.NewTimer

A new timer will be created and a channel message will be sent after the time d you set.

(2)for + select

Blocking select waiting for channel

(3)t1.Reset

The timer will be reset to restart the timer.
(note that this article applies to “t c has been removed, Reset can be used directly”)


In general, this program was written to block the main program. I hope you can think with doubt, is there any other way?

Yes, you doselect{}This requirement can also be fulfilled:)

Verification

$ go run cron.go 
2018/04/29 17:03:34 [info] replacing callback `gorm:update_time_stamp` from /Users/eddycjy/go/src/github.com/EDDYCJY/go-gin-example/models/models.go:56
2018/04/29 17:03:34 [info] replacing callback `gorm:update_time_stamp` from /Users/eddycjy/go/src/github.com/EDDYCJY/go-gin-example/models/models.go:57
2018/04/29 17:03:34 [info] replacing callback `gorm:delete` from /Users/eddycjy/go/src/github.com/EDDYCJY/go-gin-example/models/models.go:58
2018/04/29 17:03:34 Starting...
2018/04/29 17:03:35 Run models.CleanAllArticle...
2018/04/29 17:03:35 Run models.CleanAllTag...
2018/04/29 17:03:36 Run models.CleanAllArticle...
2018/04/29 17:03:36 Run models.CleanAllTag...
2018/04/29 17:03:37 Run models.CleanAllTag...
2018/04/29 17:03:37 Run models.CleanAllArticle...

Check that the output log is normal, simulate the soft deleted data, and the timed task is OK

Summary

Timing tasks are very common. I hope you can know how Golang implements a simple timing task scheduling management through this article.

Can not rely on the system Crontab settings, sometime which day to use it

References

This series of sample codes

This series of catalogues