golang使用cron表达式实现定时器
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()