亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

一文詳解Golang?定時(shí)任務(wù)庫(kù)?gron?設(shè)計(jì)和原理

 更新時(shí)間:2022年08月18日 08:40:33   作者:ag9920???????  
這篇文章主要介紹了一文詳解Golang?定時(shí)任務(wù)庫(kù)?gron?設(shè)計(jì)和原理,gron是一個(gè)比較小巧、靈活的定時(shí)任務(wù)庫(kù),可以執(zhí)行定時(shí)的、周期性的任務(wù)。gron提供簡(jiǎn)潔的、并發(fā)安全的接口

 cron 簡(jiǎn)介

在 Unix-like 操作系統(tǒng)中,有一個(gè)大家都很熟悉的 cli 工具,它能夠來(lái)處理定時(shí)任務(wù),周期性任務(wù),這就是: cron。 你只需要簡(jiǎn)單的語(yǔ)法控制就能實(shí)現(xiàn)任意【定時(shí)】的語(yǔ)義。用法上可以參考一下這個(gè) Crontab Guru Editor,做的非常精巧。

簡(jiǎn)單說(shuō),每一個(gè)位都代表了一個(gè)時(shí)間維度,* 代表全集,所以,上面的語(yǔ)義是:在每天早上的4點(diǎn)05分觸發(fā)任務(wù)。

但 cron 畢竟只是一個(gè)操作系統(tǒng)級(jí)別的工具,如果定時(shí)任務(wù)失敗了,或者壓根沒啟動(dòng),cron 是沒法提醒開發(fā)者這一點(diǎn)的。并且,cron 和 正則表達(dá)式都有一種魔力,不知道大家是否感同身受,這里引用同事的一句名言:

這世界上有些語(yǔ)言非常相似: shell腳本, es查詢的那個(gè)dsl語(yǔ)言, 定時(shí)任務(wù)的crontab, 正則表達(dá)式. 他們相似就相似在每次要寫的時(shí)候基本都得重新現(xiàn)學(xué)一遍。

正巧,最近看到了 gron 這個(gè)開源項(xiàng)目,它是用 Golang 實(shí)現(xiàn)一個(gè)并發(fā)安全的定時(shí)任務(wù)庫(kù)。實(shí)現(xiàn)非常簡(jiǎn)單精巧,代碼量也不多。今天我們就來(lái)一起結(jié)合源碼看一下,怎樣基于 Golang 的能力做出來(lái)一個(gè)【定時(shí)任務(wù)庫(kù)】。

gron

Gron provides a clear syntax for writing and deploying cron jobs.

gron 是一個(gè)泰國(guó)小哥在 2016 年開源的作品,它的特點(diǎn)就在于非常簡(jiǎn)單和清晰的語(yǔ)義來(lái)定義【定時(shí)任務(wù)】,你不用再去記 cron 的語(yǔ)法。我們來(lái)看下作為使用者怎樣上手。

首先,我們還是一個(gè) go get 安裝依賴:

$ go get github.com/roylee0704/gron

假設(shè)我們期望在【時(shí)機(jī)】到了以后,要做的工作是打印一個(gè)字符串,每一個(gè)小時(shí)執(zhí)行一次,我們就可以這樣:

package main

import (
	"fmt"
	"time"
	"github.com/roylee0704/gron"
)
func main() {
	c := gron.New()
	c.AddFunc(gron.Every(1*time.Hour), func() {
		fmt.Println("runs every hour.")
	})
	c.Start()
}

非常簡(jiǎn)單,而且即便是在 c.Start 之后我們依然可以添加新的定時(shí)任務(wù)進(jìn)去。支持了很好的擴(kuò)展性。

定時(shí)參數(shù)

注意到我們調(diào)用 gron.New().AddFunc() 時(shí)傳入了一個(gè) gron.Every(1*time.Hour)

這里其實(shí)你可以傳入任何一個(gè) time.Duration,從而把調(diào)度間隔從 1 小時(shí)調(diào)整到 1 分鐘甚至 1 秒。

除此之外,gron 還很貼心地封裝了一個(gè) xtime 包用來(lái)把常見的 time.Duration 封裝起來(lái),這里我們開箱即用。

import "github.com/roylee0704/gron/xtime"

gron.Every(1 * xtime.Day)
gron.Every(1 * xtime.Week)

很多時(shí)候我們不僅僅某個(gè)任務(wù)在當(dāng)天運(yùn)行,還希望是我們指定的時(shí)刻,而不是依賴程序啟動(dòng)時(shí)間,機(jī)械地加 24 hour。gron 對(duì)此也做了很好的支持:

gron.Every(30 * xtime.Day).At("00:00")
gron.Every(1 * xtime.Week).At("23:59")

我們只需指定 At("hh:mm") 就可以實(shí)現(xiàn)在指定時(shí)間執(zhí)行。

源碼解析

這一節(jié)我們來(lái)看看 gron 的實(shí)現(xiàn)原理。

所謂定時(shí)任務(wù),其實(shí)包含兩個(gè)層面:

  • 觸發(fā)器。即我們希望這個(gè)任務(wù)在什么時(shí)間點(diǎn),什么周期被觸發(fā);
  • 任務(wù)。即我們?cè)谟|發(fā)之后,希望執(zhí)行的任務(wù),類比到我們上面示例的 fmt.Println。

對(duì)這兩個(gè)概念的封裝和擴(kuò)展是一個(gè)定時(shí)任務(wù)庫(kù)必須考慮的。

而同時(shí),我們是在 Golang 的協(xié)程上跑程序的,意味著這會(huì)是一個(gè)長(zhǎng)期運(yùn)行的協(xié)程,否則你即便指定了【一個(gè)月后干XXX】這個(gè)任務(wù),程序兩天后掛了,也就無(wú)法實(shí)現(xiàn)你的訴求了。

所以,我們還希望有一個(gè) manager 的角色,來(lái)管理我們的一組【定時(shí)任務(wù)】,如何調(diào)度,什么時(shí)候啟動(dòng),怎么停止,啟動(dòng)了以后還想加新任務(wù)是否支持。

Cron

在 gron 的體系里,Cron 對(duì)象(我們上面通過(guò) gron.New 創(chuàng)建出來(lái)的)就是我們的 manager,而底層的一個(gè)個(gè)【定時(shí)任務(wù)】則對(duì)應(yīng)到 Cron 對(duì)象中的一個(gè)個(gè) Entry:

// Cron provides a convenient interface for scheduling job such as to clean-up
// database entry every month.
//
// Cron keeps track of any number of entries, invoking the associated func as
// specified by the schedule. It may also be started, stopped and the entries
// may be inspected.
type Cron struct {
	entries []*Entry
	running bool
	add     chan *Entry
	stop    chan struct{}
}

// New instantiates new Cron instant c.
func New() *Cron {
	return &Cron{
		stop: make(chan struct{}),
		add:  make(chan *Entry),
	}
}
  • entries 就是定時(shí)任務(wù)的核心能力,它記錄了一組【定時(shí)任務(wù)】;
  • running 用來(lái)標(biāo)識(shí)這個(gè) Cron 是否已經(jīng)啟動(dòng);
  • add 是一個(gè)channel,用來(lái)支持在 Cron 啟動(dòng)后,新增的【定時(shí)任務(wù)】;
  • stop 同樣是個(gè)channel,注意到是空結(jié)構(gòu)體,用來(lái)控制 Cron 的停止。這個(gè)其實(shí)是經(jīng)典寫法了,對(duì)日常開發(fā)也有借鑒意義,我們待會(huì)兒會(huì)好好看一下。

我們觀察到,當(dāng)調(diào)用 gron.New() 方法后,得到的是一個(gè)指向 Cron 對(duì)象的指針。此時(shí)只是初始化了 stop 和 add 兩個(gè) channel,沒有啟動(dòng)調(diào)度。

Entry

重頭戲來(lái)了,Cron 里面的 []*Entry 其實(shí)就代表了一組【定時(shí)任務(wù)】,每個(gè)【定時(shí)任務(wù)】可以簡(jiǎn)化理解為 <觸發(fā)器,任務(wù)> 組成的一個(gè) tuple。

// Entry consists of a schedule and the job to be executed on that schedule.
type Entry struct {
	Schedule Schedule
	Job      Job

	// the next time the job will run. This is zero time if Cron has not been
	// started or invalid schedule.
	Next time.Time

	// the last time the job was run. This is zero time if the job has not been
	// run.
	Prev time.Time
}

// Schedule is the interface that wraps the basic Next method.
//
// Next deduces next occurring time based on t and underlying states.
type Schedule interface {
	Next(t time.Time) time.Time
}

// Job is the interface that wraps the basic Run method.
//
// Run executes the underlying func.
type Job interface {
	Run()
}
  • Schedule 代表了一個(gè)【觸發(fā)器】,或者說(shuō)一個(gè)定時(shí)策略。它只包含一個(gè) Next 方法,接受一個(gè)時(shí)間點(diǎn),業(yè)務(wù)要返回下一次觸發(fā)調(diào)動(dòng)的時(shí)間點(diǎn)。
  • Job 則是對(duì)【任務(wù)】的抽象,只需要實(shí)現(xiàn)一個(gè) Run 方法,沒有入?yún)⒊鰠ⅰ?/li>

除了這兩個(gè)核心依賴外,Entry 結(jié)構(gòu)還包含了【前一次執(zhí)行時(shí)間點(diǎn)】和【下一次執(zhí)行時(shí)間點(diǎn)】,這個(gè)目前可以忽略,只是為了輔助代碼用。

按照時(shí)間排序

// byTime is a handy wrapper to chronologically sort entries.
type byTime []*Entry

func (b byTime) Len() int      { return len(b) }
func (b byTime) Swap(i, j int) { b[i], b[j] = b[j], b[i] }

// Less reports `earliest` time i should sort before j.
// zero time is not `earliest` time.
func (b byTime) Less(i, j int) bool {

	if b[i].Next.IsZero() {
		return false
	}
	if b[j].Next.IsZero() {
		return true
	}

	return b[i].Next.Before(b[j].Next)
}

這里是對(duì) Entry 列表的簡(jiǎn)單封裝,因?yàn)槲覀兛赡芡瑫r(shí)有多個(gè) Entry 需要調(diào)度,處理的順序很重要。這里實(shí)現(xiàn)了 sort 的接口, 有了 Len()Swap()Less() 我們就可以用 sort.Sort() 來(lái)排序了。

此處的排序策略是按照時(shí)間大小。

新增定時(shí)任務(wù)

我們?cè)谑纠锩娉霈F(xiàn)過(guò)調(diào)用 AddFunc() 來(lái)加入一個(gè) gron.Every(xxx) 這樣一個(gè)【定時(shí)任務(wù)】。其實(shí)這是給用戶提供的簡(jiǎn)單封裝。

// JobFunc is an adapter to allow the use of ordinary functions as gron.Job
// If f is a function with the appropriate signature, JobFunc(f) is a handler
// that calls f.
//
// todo: possibly func with params? maybe not needed.
type JobFunc func()

// Run calls j()
func (j JobFunc) Run() {
	j()
}


// AddFunc registers the Job function for the given Schedule.
func (c *Cron) AddFunc(s Schedule, j func()) {
	c.Add(s, JobFunc(j))
}

// Add appends schedule, job to entries.
//
// if cron instant is not running, adding to entries is trivial.
// otherwise, to prevent data-race, adds through channel.
func (c *Cron) Add(s Schedule, j Job) {

	entry := &Entry{
		Schedule: s,
		Job:      j,
	}

	if !c.running {
		c.entries = append(c.entries, entry)
		return
	}
	c.add <- entry
}

JobFunc 實(shí)現(xiàn)了我們上一節(jié)提到的 Job 接口,基于此,我們就可以讓用戶直接傳入一個(gè) func() 就ok,內(nèi)部轉(zhuǎn)成 JobFunc,再利用通用的 Add 方法將其加入到 Cron 中即可。

注意,這里的 Add 方法就是新增定時(shí)任務(wù)的核心能力了,我們需要觸發(fā)器 Schedule,任務(wù) Job。并以此來(lái)構(gòu)造出一個(gè)定時(shí)任務(wù) Entry。

若 Cron 實(shí)例還沒啟動(dòng),加入到 Cron 的 entries 列表里就ok,隨后啟動(dòng)的時(shí)候會(huì)處理。但如果已經(jīng)啟動(dòng)了,就直接往 add 這個(gè) channel 中塞,走額外的新增調(diào)度路徑。

啟動(dòng)和停止

// Start signals cron instant c to get up and running.
func (c *Cron) Start() {
	c.running = true
	go c.run()
}


// Stop halts cron instant c from running.
func (c *Cron) Stop() {

	if !c.running {
		return
	}
	c.running = false
	c.stop <- struct{}{}
}

我們先 high level 地看一下一個(gè) Cron 的啟動(dòng)和停止。

  • Start 方法執(zhí)行的時(shí)候會(huì)先將 running 變量置為 true,用來(lái)標(biāo)識(shí)實(shí)例已經(jīng)啟動(dòng)(啟動(dòng)前后加入的定時(shí)任務(wù) Entry 處理策略是不同的,所以這里需要標(biāo)識(shí)),然后啟動(dòng)一個(gè) goroutine 來(lái)實(shí)際跑啟動(dòng)的邏輯。
  • Stop 方法則會(huì)將 running 置為 false,然后直接往 stop channel 塞一個(gè)空結(jié)構(gòu)體即可。

ok,有了這個(gè)心里預(yù)期,我們來(lái)看看 c.run() 里面干了什么事:

var after = time.After


// run the scheduler...
//
// It needs to be private as it's responsible of synchronizing a critical
// shared state: `running`.
func (c *Cron) run() {

	var effective time.Time
	now := time.Now().Local()

	// to figure next trig time for entries, referenced from now
	for _, e := range c.entries {
		e.Next = e.Schedule.Next(now)
	}

	for {
		sort.Sort(byTime(c.entries))
		if len(c.entries) > 0 {
			effective = c.entries[0].Next
		} else {
			effective = now.AddDate(15, 0, 0) // to prevent phantom jobs.
		}

		select {
		case now = <-after(effective.Sub(now)):
			// entries with same time gets run.
			for _, entry := range c.entries {
				if entry.Next != effective {
					break
				}
				entry.Prev = now
				entry.Next = entry.Schedule.Next(now)
				go entry.Job.Run()
			}
		case e := <-c.add:
			e.Next = e.Schedule.Next(time.Now())
			c.entries = append(c.entries, e)
		case <-c.stop:
			return // terminate go-routine.
		}
	}
}

重點(diǎn)來(lái)了,看看我們是如何把上面 Cron, Entry, Schedule, Job 串起來(lái)的。

  • 首先拿到 local 的時(shí)間 now;
  • 遍歷所有 Entry,調(diào)用 Next 方法拿到各個(gè)【定時(shí)任務(wù)】下一次運(yùn)行的時(shí)間點(diǎn);
  • 對(duì)所有 Entry 按照時(shí)間排序(我們上面提過(guò)的 byTime);
  • 拿到第一個(gè)要到期的時(shí)間點(diǎn),在 select 里面通過(guò) time.After 來(lái)監(jiān)聽。到點(diǎn)了就起動(dòng)新的 goroutine 跑對(duì)應(yīng) entry 里的 Job,并回到 for 循環(huán),繼續(xù)重新 sort,再走同樣的流程;
  • 若 add channel 里有新的 Entry 被加進(jìn)來(lái),就加入到 Cron 的 entries 里,觸發(fā)新的 sort;
  • 若 stop channel 收到了信號(hào),就直接 return,結(jié)束執(zhí)行。

整體實(shí)現(xiàn)還是非常簡(jiǎn)潔的,大家可以感受一下。

Schedule

前面其實(shí)我們暫時(shí)將觸發(fā)器的復(fù)雜性封裝在 Schedule 接口中了,但怎么樣實(shí)現(xiàn)一個(gè) Schedule 呢?

尤其是注意,我們還支持 At 操作,也就是指定 Day,和具體的小時(shí),分鐘。回憶一下:

gron.Every(30 * xtime.Day).At("00:00")
gron.Every(1 * xtime.Week).At("23:59")

這一節(jié)我們就來(lái)看看,gron.Every 干了什么事,又是如何支持 At 方法的。

// Every returns a Schedule reoccurs every period p, p must be at least
// time.Second.
func Every(p time.Duration) AtSchedule {

	if p < time.Second {
		p = xtime.Second
	}

	p = p - time.Duration(p.Nanoseconds())%time.Second // truncates up to seconds

	return &periodicSchedule{
		period: p,
	}
}

gron 的 Every 函數(shù)接受一個(gè) time.Duration,返回了一個(gè) AtSchedule 接口。我待會(huì)兒會(huì)看,這里注意,Every 里面是會(huì)把【秒】級(jí)以下給截掉。

我們先來(lái)看下,最后返回的這個(gè) periodicSchedule 是什么:

type periodicSchedule struct {
	period time.Duration
}

// Next adds time t to underlying period, truncates up to unit of seconds.
func (ps periodicSchedule) Next(t time.Time) time.Time {
	return t.Truncate(time.Second).Add(ps.period)
}

// At returns a schedule which reoccurs every period p, at time t(hh:ss).
//
// Note: At panics when period p is less than xtime.Day, and error hh:ss format.
func (ps periodicSchedule) At(t string) Schedule {
	if ps.period < xtime.Day {
		panic("period must be at least in days")
	}

	// parse t naively
	h, m, err := parse(t)

	if err != nil {
		panic(err.Error())
	}

	return &atSchedule{
		period: ps.period,
		hh:     h,
		mm:     m,
	}
}

// parse naively tokenises hours and minutes.
//
// returns error when input format was incorrect.
func parse(hhmm string) (hh int, mm int, err error) {

	hh = int(hhmm[0]-'0')*10 + int(hhmm[1]-'0')
	mm = int(hhmm[3]-'0')*10 + int(hhmm[4]-'0')

	if hh < 0 || hh > 24 {
		hh, mm = 0, 0
		err = errors.New("invalid hh format")
	}
	if mm < 0 || mm > 59 {
		hh, mm = 0, 0
		err = errors.New("invalid mm format")
	}

	return
}

可以看到,所謂 periodicSchedule 就是一個(gè)【周期性觸發(fā)器】,只維護(hù)一個(gè) time.Duration 作為【周期】。

periodicSchedule 實(shí)現(xiàn) Next 的方式也很簡(jiǎn)單,把秒以下的截掉之后,直接 Add(period),把周期加到當(dāng)前的 time.Time 上,返回新的時(shí)間點(diǎn)。這個(gè)大家都能想到。

重點(diǎn)在于,對(duì) At 能力的支持。我們來(lái)關(guān)注下 func (ps periodicSchedule) At(t string) Schedule 這個(gè)方法

  • 若周期連 1 天都不到,不支持 At 能力,因?yàn)?At 本質(zhì)是在選定的一天內(nèi),指定小時(shí),分鐘,作為輔助。連一天都不到的周期,是要精準(zhǔn)處理的;
  • 將用戶輸入的形如 "23:59" 時(shí)間字符串解析出來(lái)【小時(shí)】和【分鐘】;
  • 構(gòu)建出一個(gè) atSchedule 對(duì)象,包含了【周期時(shí)長(zhǎng)】,【小時(shí)】,【分鐘】。

ok,這一步只是拿到了材料,那具體怎樣處理呢?這個(gè)還是得繼續(xù)往下走,看看 atSchedule 結(jié)構(gòu)干了什么:

type atSchedule struct {
	period time.Duration
	hh     int
	mm     int
}

// reset returns new Date based on time instant t, and reconfigure its hh:ss
// according to atSchedule's hh:ss.
func (as atSchedule) reset(t time.Time) time.Time {
	return time.Date(t.Year(), t.Month(), t.Day(), as.hh, as.mm, 0, 0, time.UTC)
}

// Next returns **next** time.
// if t passed its supposed schedule: reset(t), returns reset(t) + period,
// else returns reset(t).
func (as atSchedule) Next(t time.Time) time.Time {
	next := as.reset(t)
	if t.After(next) {
		return next.Add(as.period)
	}
	return next
}

其實(shí)只看這個(gè) Next 的實(shí)現(xiàn)即可。我們從 periodSchedule 那里獲取了三個(gè)屬性。

在調(diào)用 Next 方法時(shí),先做 reset,根據(jù)原有 time.Time 的年,月,日,以及用戶輸入的 At 中的小時(shí),分鐘,來(lái)構(gòu)建出來(lái)一個(gè) time.Time 作為新的時(shí)間點(diǎn)。

此后判斷是在哪個(gè)周期,如果當(dāng)前周期已經(jīng)過(guò)了,那就按照下個(gè)周期的時(shí)間點(diǎn)返回。

到這里,一切就都清楚了,如果我們不用 At 能力,直接 gron.Every(xxx),那么直接就會(huì)調(diào)用

t.Truncate(time.Second).Add(ps.period)

拿到一個(gè)新的時(shí)間點(diǎn)返回。

而如果我們要用 At 能力,指定當(dāng)天的小時(shí),分鐘。那就會(huì)走到 periodicSchedule.At 這里,解析出【小時(shí)】和【分鐘】,最后走 Next 返回 reset 之后的時(shí)間點(diǎn)。

這個(gè)和 gron.Every 方法返回的 AtSchedule 接口其實(shí)是完全對(duì)應(yīng)的:

// AtSchedule extends Schedule by enabling periodic-interval & time-specific setup
type AtSchedule interface {
	At(t string) Schedule
	Schedule
}

直接就有一個(gè) Schedule 可以用,但如果你想針對(duì)天級(jí)以上的 duration 指定時(shí)間,也可以走 At 方法,也會(huì)返回一個(gè) Schedule 供我們使用。

擴(kuò)展性

gron 里面對(duì)于所有的依賴也都做成了【依賴接口而不是實(shí)現(xiàn)】。Cron 的 Add 函數(shù)的入?yún)⒁彩莾蓚€(gè)接口,這里可以隨意替換:func (c *Cron) Add(s Schedule, j Job)。

最核心的兩個(gè)實(shí)體依賴 Schedule, Job 都可以用你自定義的實(shí)現(xiàn)來(lái)替換掉。

如實(shí)現(xiàn)一個(gè)新的 Job:

type Reminder struct {
	Msg string
}

func (r Reminder) Run() {
  fmt.Println(r.Msg)
}

事實(shí)上,我們上面提到的 periodicSchedule 以及 atSchedule 就是 Schedule 接口的具體實(shí)現(xiàn)。我們也完全可以不用 gron.Every,而是自己寫一套新的 Schedule 實(shí)現(xiàn)。只要實(shí)現(xiàn) Next(p time.Duration) time.Time 即可。

我們來(lái)看一個(gè)完整用法案例:

package main

import (
	"fmt"
	"github.com/roylee0704/gron"
	"github.com/roylee0704/gron/xtime"
)
type PrintJob struct{ Msg string }
func (p PrintJob) Run() {
	fmt.Println(p.Msg)
}

func main() {

	var (
		// schedules
		daily     = gron.Every(1 * xtime.Day)
		weekly    = gron.Every(1 * xtime.Week)
		monthly   = gron.Every(30 * xtime.Day)
		yearly    = gron.Every(365 * xtime.Day)

		// contrived jobs
		purgeTask = func() { fmt.Println("purge aged records") }
		printFoo  = printJob{"Foo"}
		printBar  = printJob{"Bar"}
	)

	c := gron.New()

	c.Add(daily.At("12:30"), printFoo)
	c.AddFunc(weekly, func() { fmt.Println("Every week") })
	c.Start()

	// Jobs may also be added to a running Gron
	c.Add(monthly, printBar)
	c.AddFunc(yearly, purgeTask)

	// Stop Gron (running jobs are not halted).
	c.Stop()
}

經(jīng)典寫法-控制退出

這里我們還是要聊一下 Cron 里控制退出的經(jīng)典寫法。我們把其他不相關(guān)的部分清理掉,只留下核心代碼:

type Cron struct {
	stop    chan struct{}
}

func (c *Cron) Stop() {
	c.stop <- struct{}{}
}

func (c *Cron) run() {

	for {
		select {
		case <-c.stop:
			return // terminate go-routine.
		}
	}
}

空結(jié)構(gòu)體能夠最大限度節(jié)省內(nèi)存,畢竟我們只是需要一個(gè)信號(hào)。核心邏輯用 for + select 的配合,這樣當(dāng)我們需要結(jié)束時(shí)可以立刻響應(yīng)。非常經(jīng)典,建議大家日常有需要的時(shí)候采用。

結(jié)語(yǔ)

gron 整體代碼其實(shí)只在 cron.go 和 schedule.go 兩個(gè)文件,合起來(lái)代碼不過(guò) 300 行,非常精巧,基本沒有冗余,擴(kuò)展性很好,是非常好的入門材料。

不過(guò),作為一個(gè) cron 的替代品,其實(shí) gron 還是有自己的問(wèn)題的。簡(jiǎn)單講就是,如果我重啟了一個(gè)EC2實(shí)例,那么我的 cron job 其實(shí)也還會(huì)繼續(xù)執(zhí)行,這是落盤的,操作系統(tǒng)級(jí)別的支持。

但如果我執(zhí)行 gron 的進(jìn)程掛掉了,不好意思,那就完全涼了。你只有重啟,然后再把所有任務(wù)加回來(lái)才行。而我們既然要用 gron,是很有可能定一個(gè)幾天后,幾個(gè)星期后,幾個(gè)月后這樣的觸發(fā)器的。誰(shuí)能保證進(jìn)程一直活著呢?連機(jī)子本身都可能重啟。

所以,我們需要一定的機(jī)制來(lái)保證 gron 任務(wù)的可恢復(fù)性,將任務(wù)落盤,持久化狀態(tài)信息,算是個(gè)思考題,這里大家可以考慮一下怎么做。

到此這篇關(guān)于一文詳解Golang 定時(shí)任務(wù)庫(kù) gron 設(shè)計(jì)和原理的文章就介紹到這了,更多相關(guān)Golang   gron內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

  • 一文了解Go語(yǔ)言io.Copy函數(shù)

    一文了解Go語(yǔ)言io.Copy函數(shù)

    這篇文章主要為大家介紹了Go語(yǔ)言io.Copy函數(shù)使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • Go錯(cuò)誤處理的幾種方式

    Go錯(cuò)誤處理的幾種方式

    在Go語(yǔ)言中,錯(cuò)誤處理是一種重要的編程模式,它用于處理可能出現(xiàn)的錯(cuò)誤或異常情況,本文就來(lái)介紹一下Go錯(cuò)誤處理的幾種方式,感興趣的可以了解一下
    2023-11-11
  • Golang?Makefile示例深入講解使用

    Golang?Makefile示例深入講解使用

    一次偶然的機(jī)會(huì),在?github?上看到有人用?Makefile,就嘗試了一下,發(fā)現(xiàn)真的非常合適,Makefile?本身就是用來(lái)描述依賴的,可讀性非常好,而且與強(qiáng)大的?shell?結(jié)合在一起,基本可以實(shí)現(xiàn)任何想要的功能
    2023-01-01
  • 如何使用Go語(yǔ)言獲取當(dāng)天、昨天、明天、某天0點(diǎn)時(shí)間戳以及格式化時(shí)間

    如何使用Go語(yǔ)言獲取當(dāng)天、昨天、明天、某天0點(diǎn)時(shí)間戳以及格式化時(shí)間

    這篇文章主要給大家介紹了關(guān)于如何使用Go語(yǔ)言獲取當(dāng)天、昨天、明天、某天0點(diǎn)時(shí)間戳以及格式化時(shí)間的相關(guān)資料,格式化時(shí)間戳是將時(shí)間戳轉(zhuǎn)換為特定的日期和時(shí)間格式,文中通過(guò)代碼示例介紹的非常詳細(xì),需要的朋友可以參考下
    2023-10-10
  • Golang通脈之流程控制詳情

    Golang通脈之流程控制詳情

    這篇文章主要介紹了Golang通脈之流程控制,流程控制是每種編程語(yǔ)言控制邏輯走向和執(zhí)行次序的重要部分,Go語(yǔ)言中最常用的流程控制有if和for,而switch和goto主要是為了簡(jiǎn)化代碼,下面文章將詳細(xì)介紹改該內(nèi)容,需要的朋友可以參考一下
    2021-10-10
  • Go疑難雜癥講解之為什么nil不等于nil

    Go疑難雜癥講解之為什么nil不等于nil

    在日常開發(fā)中,可能一不小心就會(huì)掉進(jìn)?Go?語(yǔ)言的某些陷阱里,而本文要介紹的?nil?≠?nil?問(wèn)題,感興趣的小伙伴可以跟隨小編一起了解一下
    2022-10-10
  • Golang與其他語(yǔ)言不同的九個(gè)特性

    Golang與其他語(yǔ)言不同的九個(gè)特性

    近來(lái)關(guān)于對(duì)Golang的討論有很多,七牛的幾個(gè)大牛們也斷定Go語(yǔ)言在未來(lái)將會(huì)快速發(fā)展,并且很可能會(huì)取代Java成為互聯(lián)網(wǎng)時(shí)代最受歡迎的編程語(yǔ)言。本文將帶你了解它不同于其他語(yǔ)言的九個(gè)特性
    2021-09-09
  • Go語(yǔ)言標(biāo)準(zhǔn)輸入輸出庫(kù)的基本使用教程

    Go語(yǔ)言標(biāo)準(zhǔn)輸入輸出庫(kù)的基本使用教程

    輸入輸出在任何一門語(yǔ)言中都必須提供的一個(gè)功能,下面這篇文章主要給大家介紹了關(guān)于Go語(yǔ)言標(biāo)準(zhǔn)輸入輸出庫(kù)的基本使用,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-02-02
  • 多階段構(gòu)建優(yōu)化Go?程序Docker鏡像

    多階段構(gòu)建優(yōu)化Go?程序Docker鏡像

    這篇文章主要為大家介紹了多階段構(gòu)建優(yōu)化Go?程序Docker鏡像,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • 最新評(píng)論