欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Golang如何對(duì)cron進(jìn)行二次封裝實(shí)現(xiàn)指定時(shí)間執(zhí)行定時(shí)任務(wù)

 更新時(shí)間:2025年06月25日 14:08:59   作者:在成都搬磚的鴨鴨  
這篇文章主要介紹了Golang如何對(duì)cron進(jìn)行二次封裝實(shí)現(xiàn)指定時(shí)間執(zhí)行定時(shí)任務(wù)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

背景

go中常用的定時(shí)任務(wù)庫(kù):https://github.com/robfig/cron,不支持指定某個(gè)時(shí)間開始執(zhí)行一次任務(wù),然后再以一定的時(shí)間間隔開始執(zhí)行任務(wù)。

在真實(shí)的業(yè)務(wù)場(chǎng)景中可能會(huì)有這樣的需求:進(jìn)行某個(gè)操作時(shí)執(zhí)行一次任務(wù),然后根據(jù)這個(gè)操作時(shí)間每隔一段時(shí)間執(zhí)行一次任務(wù);再次操作時(shí),立即執(zhí)行一次任務(wù),然后根據(jù)此次操作的時(shí)間點(diǎn)每隔一段時(shí)間執(zhí)行一次任務(wù),這時(shí)就需要停止上一次操作產(chǎn)生的定時(shí)間隔執(zhí)行任務(wù)。

下面就根據(jù)go中的cron庫(kù)進(jìn)行封裝來(lái)滿足上述場(chǎng)景的功能。

cron庫(kù)下載

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

代碼示例

【1】結(jié)構(gòu)體定義

import (
	"fmt"
	"sync"
	"time"

	"GoTest/comm/logger"

	"github.com/robfig/cron/v3"
	"go.uber.org/zap"
)

const (
	HOUR   = 1
	MINUTE = 2
	SECOND = 3
)

const defaultIntervalTime = 8

type VarCron struct {
	c            *cron.Cron
	jobId        cron.EntryID //執(zhí)行的任務(wù)id
	jobMutex     sync.Mutex   //防止并發(fā)問題
	startTime    time.Time    //定時(shí)任務(wù)從此時(shí)間開始執(zhí)行
	intervalTime uint32       //任務(wù)執(zhí)行間隔時(shí)間,不設(shè)置默認(rèn)8小時(shí)
	intervalType uint8        //任務(wù)執(zhí)行間隔類型,1-時(shí),2.分,3-秒,不設(shè)置默認(rèn)單位為時(shí)
	f            func() error //要執(zhí)行的任務(wù)
}

type OpOption func(*VarCron)

func NewVarCron(f func() error, opts ...OpOption) *VarCron {
	vc := &VarCron{
		c: cron.New(cron.WithSeconds()), //支持秒級(jí)調(diào)度
		f: f,
	}

	for _, opt := range opts {
		opt(vc)
	}

	return vc
}

func WithIntervalTime(intervalTime uint32) OpOption {
	return func(vc *VarCron) {
		vc.intervalTime = intervalTime
	}
}

func WithIntervalType(intervalType uint8) OpOption {
	return func(vc *VarCron) {
		vc.intervalType = intervalType
	}
}

上面定義一個(gè)新的結(jié)構(gòu)體VarCron就是給原生的cron對(duì)象增加一些新的屬性字段。

字段含義
jobId定時(shí)執(zhí)行任務(wù)的id,只支持一個(gè)任務(wù),目的是為了方便管理,如果想支持多個(gè),可以多使用NewVarCron初始化多個(gè)對(duì)象分別執(zhí)行各自的定時(shí)任務(wù)
jobMutex保證并發(fā)安全
startTime首次執(zhí)行任務(wù)的時(shí)間
intervalTime定時(shí)任務(wù)執(zhí)行間隔
intervalType定時(shí)任務(wù)執(zhí)行類型
f定時(shí)任務(wù)中執(zhí)行的函數(shù)

【2】定時(shí)任務(wù)開啟

func (v *VarCron) Start(startTime time.Time) {
	v.jobMutex.Lock() //保證同一時(shí)刻只有一個(gè)在執(zhí)行
	defer v.jobMutex.Unlock()

	logger.Info("start var cron", zap.Time("last_start_time", v.startTime), zap.Time("this_start_time", startTime))

	//更新開始時(shí)間
	v.startTime = startTime

	//停止上一次的定時(shí)任務(wù)
	v.stop()

	now := time.Now()
	if now.After(v.startTime) || now.Equal(v.startTime) { //定時(shí)任務(wù)指定的執(zhí)行時(shí)間在此刻之前就立即執(zhí)行
		logger.Info("now time after or equal start time", zap.Time("now_time", now), zap.Time("start_time", v.startTime))

		//立即執(zhí)行一次
		v.execJob()

		//間隔執(zhí)行定時(shí)任務(wù)
		jobId, err := v.c.AddFunc(v.getInterval(), func() {
			v.execJob()
		})
		if err != nil {
			logger.Error("add func error", zap.Error(err))
			return
		}

		v.jobId = jobId

		//開始定時(shí)任務(wù)
		v.c.Start()
	} else {
		logger.Info("now time before start time", zap.Time("now_time", now), zap.Time("start_time", v.startTime))

		//到指定時(shí)間時(shí)間之后再開始執(zhí)行定時(shí)任務(wù),精確到某月某天某時(shí)某分某秒就行
		jobId, err := v.c.AddFunc(fmt.Sprintf("%d %d %d %d %d ?", v.startTime.Second(), v.startTime.Minute(), v.startTime.Hour(), v.startTime.Day(), v.startTime.Month()), func() {
			//停止上一次的定時(shí)任務(wù)
			v.stop()

			//這次是到指定時(shí)間之后的執(zhí)行
			v.execJob()

			//根據(jù)間隔開啟定時(shí)任務(wù)
			newJobId, err := v.c.AddFunc(v.getInterval(), func() {
				v.execJob()
			})
			if err != nil {
				logger.Error("add func error", zap.Error(err))
				return
			}

			v.jobId = newJobId
			v.c.Start()
		})
		if err != nil {
			logger.Error("add func error", zap.Error(err))
			return
		}

		v.jobId = jobId
		v.c.Start()
	}
}

func (v *VarCron) stop() {
	if v.c != nil {
		v.c.Remove(v.jobId) //移除任務(wù)
		v.c.Stop()          //關(guān)閉定時(shí)任務(wù)
	}
}

func (v *VarCron) execJob() {
	if err := v.f(); err != nil {
		logger.Error("exec job error", zap.Error(err))
	}
}

func (v *VarCron) getInterval() string {
	if v.intervalTime == 0 {
		v.intervalTime = defaultIntervalTime
	}

	switch v.intervalType {
	case HOUR:
		return fmt.Sprintf("@every %dh", v.intervalTime)
	case MINUTE:
		return fmt.Sprintf("@every %dm", v.intervalTime)
	case SECOND:
		return fmt.Sprintf("@every %ds", v.intervalTime)
	}

	return fmt.Sprintf("@every %dh", v.intervalTime)
}

調(diào)用Start(startTime time.Time)函數(shù)會(huì)有兩種場(chǎng)景:

  • 場(chǎng)景1:startTime在當(dāng)前時(shí)間之前,這個(gè)時(shí)候會(huì)立即執(zhí)行一次任務(wù),然后按照間隔定時(shí)執(zhí)行任務(wù)。
  • 場(chǎng)景2:startTime在當(dāng)前時(shí)間之后,這個(gè)時(shí)候會(huì)等待時(shí)間到達(dá)startTime執(zhí)行一次,然后按照間隔定時(shí)執(zhí)行任務(wù)。

上面代碼中的stop函數(shù)目的就是為了停止上一次的任務(wù),保證不會(huì)有沖突的定時(shí)任務(wù)執(zhí)行。

【3】使用示例

f := func() error {
		logger.Info("exec task")

		return nil
	}

	//每30秒打印一次時(shí)間
	varCron := self_cron.NewVarCron(f, self_cron.WithIntervalType(self_cron.SECOND), self_cron.WithIntervalTime(30))

	fmt.Println("===== 指定開始時(shí)間在當(dāng)前時(shí)間之前,會(huì)立即執(zhí)行一次,然后定時(shí)執(zhí)行 ======")
	varCron.Start(time.Now().AddDate(0, 0, -1)) //AddDate(0, 0, -1)的作用是將日期向前推一天

	time.Sleep(3 * time.Minute) //第一種場(chǎng)景定時(shí)執(zhí)行任務(wù)跑三分鐘

	fmt.Println("===== 指定開始時(shí)間在當(dāng)前時(shí)間之后,到指定時(shí)間執(zhí)行一次,然后再開始定時(shí)執(zhí)行 ======")
	varCron.Start(time.Now().Add(time.Minute)) //一分鐘后立即執(zhí)行一次,然后開始定時(shí)執(zhí)行

	time.Sleep(3 * time.Minute) //第二種場(chǎng)景定時(shí)執(zhí)行任務(wù)跑三分鐘就退出

上面是一個(gè)簡(jiǎn)單使用的例子:注冊(cè)一個(gè)每隔30秒打印字符串exec task的函數(shù);第1次指定開始時(shí)間為昨天的這個(gè)時(shí)刻,這時(shí)會(huì)立即打印exec task一次,然后每隔30s打印一次exec task;3分鐘后第2次指定開始時(shí)間為當(dāng)前時(shí)間的1分鐘之后,這時(shí)上一次的任務(wù)已經(jīng)被取消,1分鐘內(nèi)不會(huì)再打印exec task,1分鐘后會(huì)打印exec task,然后每隔30s打印一次exec task。

【4】控制臺(tái)輸出

$ go run ./cron_demo/main.go
===== 指定開始時(shí)間在當(dāng)前時(shí)間之前,會(huì)立即執(zhí)行一次,然后定時(shí)執(zhí)行 ======
[2024-10-08 17:23:38.135] | INFO | Goroutine:1  | [self_cron/start_cron.go:63] | start var cron | {"last_start_time": "[0001-01-01 00:00:00.000]", "this_start_time": "[2024-10-07 17:23:38.096]"}
[2024-10-08 17:23:38.136] | INFO | Goroutine:1  | [self_cron/start_cron.go:73] | now time after or equal start time | {"now_time": "[2024-10-08 17:23:38.136]", "start_time": "[2024-10-07 17:23:38.096]"}
[2024-10-08 17:23:38.136] | INFO | Goroutine:1  | [cron_demo/main.go:159]      | exec task //指定的執(zhí)行時(shí)間在當(dāng)前時(shí)間之前,立即執(zhí)行一次,之后每隔30s執(zhí)行
[2024-10-08 17:24:08.013] | INFO | Goroutine:34 | [cron_demo/main.go:159]      | exec task
[2024-10-08 17:24:38.008] | INFO | Goroutine:35 | [cron_demo/main.go:159]      | exec task
[2024-10-08 17:25:08.010] | INFO | Goroutine:36 | [cron_demo/main.go:159]      | exec task
[2024-10-08 17:25:38.008] | INFO | Goroutine:37 | [cron_demo/main.go:159]      | exec task
[2024-10-08 17:26:08.015] | INFO | Goroutine:38 | [cron_demo/main.go:159]      | exec task
[2024-10-08 17:26:38.005] | INFO | Goroutine:39 | [cron_demo/main.go:159]      | exec task
===== 指定開始時(shí)間在當(dāng)前時(shí)間之后,到指定時(shí)間執(zhí)行一次,然后再開始定時(shí)執(zhí)行 ======
[2024-10-08 17:26:38.144] | INFO | Goroutine:1  | [self_cron/start_cron.go:63] | start var cron | {"last_start_time": "[2024-10-07 17:23:38.096]", "this_start_time": "[2024-10-08 17:27:38.144]"} 
[2024-10-08 17:26:38.145] | INFO | Goroutine:1  | [self_cron/start_cron.go:92] | now time before start time | {"now_time": "[2024-10-08 17:26:38.145]", "start_time": "[2024-10-08 17:27:38.144]"}
[2024-10-08 17:27:38.003] | INFO | Goroutine:5  | [cron_demo/main.go:159]      | exec task //指定時(shí)間到了之后才執(zhí)行,之后每隔30s執(zhí)行
[2024-10-08 17:28:08.007] | INFO | Goroutine:42 | [cron_demo/main.go:159]      | exec task
[2024-10-08 17:28:38.008] | INFO | Goroutine:43 | [cron_demo/main.go:159]      | exec task
[2024-10-08 17:29:08.013] | INFO | Goroutine:44 | [cron_demo/main.go:159]      | exec task
[2024-10-08 17:29:38.013] | INFO | Goroutine:45 | [cron_demo/main.go:159]      | exec task

總結(jié)

上面給出一個(gè)指定一個(gè)時(shí)間開始執(zhí)行任務(wù),然后再以一定的時(shí)間間隔定時(shí)執(zhí)行任務(wù)的功能,可以根據(jù)業(yè)務(wù)場(chǎng)景的需求去修改上述功能,新增一些屬性,比如除了動(dòng)態(tài)修改定時(shí)任務(wù)時(shí)間,還可以動(dòng)態(tài)修改時(shí)間間隔或執(zhí)行函數(shù),或者新增一個(gè)tag字段打印在日志中來(lái)區(qū)分不同的業(yè)務(wù)。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Go語(yǔ)言之fo循環(huán)與條件判斷

    Go語(yǔ)言之fo循環(huán)與條件判斷

    這篇文章主要介紹Go語(yǔ)言之fo循環(huán)與條件判斷,for 循環(huán)是一個(gè)循環(huán)控制結(jié)構(gòu),可以執(zhí)行指定次數(shù)的循環(huán)。下面文章我們結(jié)合條件判斷,感興趣的朋友可以參考一下
    2021-10-10
  • Go語(yǔ)言實(shí)現(xiàn)動(dòng)態(tài)開點(diǎn)線段樹詳解

    Go語(yǔ)言實(shí)現(xiàn)動(dòng)態(tài)開點(diǎn)線段樹詳解

    線段樹是一種用于高效處理區(qū)間查詢和區(qū)間更新的數(shù)據(jù)結(jié)構(gòu),下面我們就來(lái)看看如何使用Go實(shí)現(xiàn)動(dòng)態(tài)開點(diǎn)線段樹的方式,感興趣的可以了解下
    2025-02-02
  • Go中map數(shù)據(jù)類型的實(shí)現(xiàn)

    Go中map數(shù)據(jù)類型的實(shí)現(xiàn)

    本文介紹了Go語(yǔ)言中的map數(shù)據(jù)類型,用于高效地存儲(chǔ)和管理鍵值對(duì),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-12-12
  • 使用Go語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單聊天系統(tǒng)

    使用Go語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單聊天系統(tǒng)

    本文介紹了如何使用Go語(yǔ)言和WebSocket技術(shù)構(gòu)建一個(gè)簡(jiǎn)單的多人聊天室系統(tǒng),包括客戶端連接管理、消息廣播和并發(fā)處理,最后,通過編寫main.go、hub.go和client.go等核心代碼模塊,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-10-10
  • Golang 使用map需要注意的幾個(gè)點(diǎn)

    Golang 使用map需要注意的幾個(gè)點(diǎn)

    這篇文章主要介紹了Golang 使用map需要注意的幾個(gè)點(diǎn),幫助大家更好的理解和學(xué)習(xí)golang,感興趣的朋友可以了解下
    2020-09-09
  • Golang中的Interface詳解

    Golang中的Interface詳解

    本文詳細(xì)講解了Golang中的Interface,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07
  • Go語(yǔ)言簡(jiǎn)介和環(huán)境配置

    Go語(yǔ)言簡(jiǎn)介和環(huán)境配置

    Go語(yǔ)言保證了既能到達(dá)靜態(tài)編譯語(yǔ)言的安全和性能,又達(dá)到了動(dòng)態(tài)語(yǔ)言開發(fā)速度和易維護(hù)性,有人形容Go語(yǔ)言:Go=?C?+?Python?,?說(shuō)明Go語(yǔ)言既有C靜態(tài)語(yǔ)言程序的運(yùn)行速度,又能達(dá)到Python動(dòng)態(tài)語(yǔ)言的快速開發(fā),這篇文章主要介紹了Go介紹和環(huán)境配置,需要的朋友可以參考下
    2022-07-07
  • 深入了解Golang中的數(shù)據(jù)類型

    深入了解Golang中的數(shù)據(jù)類型

    在計(jì)算機(jī)編程中,數(shù)據(jù)類型是非常重要的一個(gè)概念。這篇文章將詳細(xì)介紹 Golang中的數(shù)據(jù)類型,包括基本類型、復(fù)合類型、引用類型以及自定義類型,希望對(duì)大家有所幫助
    2023-04-04
  • golang time包下定時(shí)器的實(shí)現(xiàn)方法

    golang time包下定時(shí)器的實(shí)現(xiàn)方法

    定時(shí)器的實(shí)現(xiàn)大家應(yīng)該都遇到過,最近在學(xué)習(xí)golang,所以下面這篇文章主要給大家介紹了關(guān)于golang time包下定時(shí)器的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來(lái)一起看看吧。
    2017-12-12
  • Golang工具庫(kù)viper的使用教程

    Golang工具庫(kù)viper的使用教程

    viper?是?go?項(xiàng)目中用來(lái)讀取配置文件的庫(kù),支持讀取?yaml、toml、json、hcl、env?等格式的配置文件,下面就來(lái)和大家聊聊它的具體使用吧
    2023-07-07

最新評(píng)論