相关文章推荐
愤怒的显示器  ·  sql - PostgreSQL - ...·  1 年前    · 

golang使用cron表达式实现定时器

1 年前 · 来自专栏 go语言学习旅程

cron表达式是我用过迄今为止最好用的定时器表达方式,比较的灵活,可以支持多种多样的灵活的定时器。

先看最简单的用法:

package main
import (
	"fmt"
	"github.com/robfig/cron"
func main() {
	c := cron.New()
	c.AddFunc("*/5 * * * * *", func() {
		fmt.Println("每5秒执行一次")
	c.Start()
	select {}

如果业务比较复杂的话我们还可以进行更深一层次的封装。

package main
import (
	"fmt"
	"github.com/robfig/cron"
func RunTimer(value string)  {
	fmt.Println(value)
	fmt.Println("每5秒执行一次")
func main() {
	c := cron.New()
	c.AddFunc("*/5 * * * * *", func() {
		RunTimer("传入参数")
	c.Start()
	select {}

我们可以添加多条任务

package main
import (
	"fmt"
	"github.com/robfig/cron"
func RunTimer(value string)  {
	fmt.Println(value)
	fmt.Println("每5秒执行一次")
func main() {
	c := cron.New()
	c.AddFunc("*/5 * * * * *", func() {
		RunTimer("传入参数")
	c.AddFunc("*/5 * * * * *", func() {
		RunTimer("第二条任务传入参数")
	c.Start()
	select {}

还可以使用stop函数来停止我们的定时器

package main
import (
	"fmt"
	"github.com/robfig/cron"
func RunTimer(value string)  {
	fmt.Println(value)
	fmt.Println("每5秒执行一次")
func main() {
	c := cron.New()
	c.AddFunc("*/5 * * * * *", func() {
		RunTimer("传入参数")
	c.AddFunc("*/5 * * * * *", func() {
		RunTimer("第二条任务传入参数")
	c.Start()
	defer c.Stop()

除此之外还有一种job的方式,不过我认为这种方式用起来不是很直观,不推荐使用


package main
import (
	"fmt"
	"github.com/robfig/cron"
type TestJob struct {
func (TestJob) Run()  {
	fmt.Println("每5秒执行一次")
func main() {
	c := cron.New()
	c.AddJob("*/5 * * * * *",TestJob{})
	c.Start()
	defer c.Stop()

除了cron表达式它还有一种自己特定的写法

package main
import (
	"fmt"
	"github.com/robfig/cron"
func main() {
	c := cron.New()
	c.AddFunc("@hourly", func() {
		fmt.Println("每个小时执行一次")
	c.Start()
	defer c.Stop()
package main
import (
	"fmt"
	"github.com/robfig/cron"
func main() {
	c := cron.New()
	c.AddFunc("@every 1h30m", func() {
		fmt.Println("每个小时的第三十分钟执行")
	c.Start()
	defer c.Stop()

不过还是有一些需要注意的点,这个库的cron表达式和网上一些常规的cron表达式有点不同






可以看到网上一些在线生成的表达式是有7位的

我尝试把它放进去

package main
import (
	"fmt"
	"github.com/robfig/cron"
func main() {
	c := cron.New()
	err:=c.AddFunc("0/1 * * * * ? *", func() {
		fmt.Println("每一秒执行一次")
	if err!=nil{
		fmt.Println(err.Error())
	c.Start()
	select {

执行结果报错了:






查看了官方文档发现他缺少了年份这一位 所以这里不能使用常规的在线生成器生成的cron表达式去操作


还有一点就是它不支持删除正在挂起的定时器任务


于是我做了一个改装


package main
import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"github.com/robfig/cron"
	uuid "github.com/satori/go.uuid"
var CronArray=make(map[string]cron.Cron)
func Get16MD5Encode(data string) string {
	return GetMD5Encode(data)[8:24]
//获取uuid
func GetUuid() string {
	u := uuid.NewV4()
	return Get16MD5Encode(u.String())
//返回一个32位md5加密后的字符串
func GetMD5Encode(data string) string {
	h := md5.New()
	h.Write([]byte(data))
	return hex.EncodeToString(h.Sum(nil))
func main() {
	c := cron.New()
	err:=c.AddFunc("0/1 * * * * ? *", func() {
		fmt.Println("每一秒执行一次")
	if err!=nil{
		fmt.Println(err.Error())
	c.Start()
	cronId:=GetUuid()
	CronArray[cronId]=*c
	select {

如此一来我就可以通过map的key值传入来执行stop函数来停止掉指定的cron定时器任务

但是这么写其实还是有风险的,当我们的程序异步竞争map时可能出现map为空的错误

所以我们在改造一下

package main
import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"github.com/robfig/cron"
	uuid "github.com/satori/go.uuid"
	"sync"
var CronArray sync.Map
func Get16MD5Encode(data string) string {
	return GetMD5Encode(data)[8:24]
//获取uuid
func GetUuid() string {
	u := uuid.NewV4()
	return Get16MD5Encode(u.String())
//返回一个32位md5加密后的字符串
func GetMD5Encode(data string) string {
	h := md5.New()
	h.Write([]byte(data))
	return hex.EncodeToString(h.Sum(nil))
func main() {
	c := cron.New()
	err:=c.AddFunc("0/1 * * * * ? *", func() {
		fmt.Println("每一秒执行一次")
	if err!=nil{
		fmt.Println(err.Error())
	c.Start()
	cronId:=GetUuid()
	//添加cron定时器
	CronArray.Store(cronId,*c)
	//获取指定cron定时器关闭
	getCron,ok:=CronArray.Load(cronId)
	if ok{
		cronNew:=getCron.(cron.Cron)
		cronNew.Stop()