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

Go語言定時(shí)任務(wù)cron的設(shè)計(jì)與使用

 更新時(shí)間:2023年11月06日 10:33:14   作者:小范真是一把好手  
這篇文章主要為大家詳細(xì)介紹了Go語言中定時(shí)任務(wù)cron的設(shè)計(jì)與使用,文中的示例代碼講解詳細(xì),對我們深入掌握Go語言有一定的幫助,需要的可以參考下

一、Cron表達(dá)式

Field name   | Mandatory? | Allowed values  | Allowed special characters
----------   | ---------- | --------------  | --------------------------
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表達(dá)式的格式是通過六個(gè)字符表示:"1 * * * * *"。這六位數(shù)分別表示秒,分,小時(shí),每月第幾天,月,每個(gè)星期第幾天;

在這里重點(diǎn)解釋一下特殊字符:

  • *:代表任意值;*在分鐘字段,表示每分鐘;
  • /:用來指定時(shí)間間隔,*/15在分鐘字段,表示每隔15分鐘;
  • ,:列出多個(gè)離散值,1,15在天字段,表示每月1號和15號;
  • -:定義某個(gè)范圍,9-17在小時(shí)字段,表示上午9點(diǎn)到下午5點(diǎn),兩邊都是閉區(qū)間;
  • ?:表示無特定值。在Cron中,如果天數(shù)與星期的指定會互斥。看下面兩個(gè)例子:

0 0 12 ? * WED - 表示每周三中午12點(diǎn)。關(guān)心星期,忽略天數(shù);

0 0 12 15 * ? - 表示每個(gè)月的第15天中午12點(diǎn)。關(guān)心天數(shù),忽略星期;

同時(shí)在"github.com/robfig/cron/v3"包中預(yù)定義的Schedule,如下所示:

Entry                  | Description                                | Equivalent To
-----                  | -----------                                | -------------
@yearly (or @annually) | Run once a year, midnight, Jan. 1st        | 0 0 0 1 1 *
@monthly               | Run once a month, midnight, first of month | 0 0 0 1 * *
@weekly                | Run once a week, midnight between Sat/Sun  | 0 0 0 * * 0
@daily (or @midnight)  | Run once a day, midnight                   | 0 0 0 * * *
@hourly                | Run once an hour, beginning of hour        | 0 0 * * * *

二、如何使用Cron包

func TestCron(t *testing.T) {
	c := cron.New(cron.WithSeconds())

	// 每分鐘第一秒執(zhí)行該任務(wù)
	c.AddFunc("1 * * * * *", func() {
		fmt.Println("Hello world!")
	})

    // 每10s執(zhí)行一次任務(wù)
	sh := cron.Every(10 * time.Second)
	c.Schedule(sh, cron.FuncJob(func() {
		fmt.Println("you are ok")
	}))

	go func() {
		ticker := time.NewTicker(time.Second * 4)
		for {
			select {
			case <-ticker.C:
				fmt.Println("length: ", len(c.Entries()))
			}
		}
	}()

	// c.Start()
	c.Start()

	// Wait for the Cron job to run
	time.Sleep(5 * time.Minute)

	// Stop the Cron job scheduler
	c.Stop()
}

上述示例代碼中,使用兩種創(chuàng)建定時(shí)任務(wù)的方式,分別是:

  • c.AddFunc()
  • c.Schedule()

cron包的使用非常簡單,你只需要提供Job以及其執(zhí)行的規(guī)則即可。

三、如何設(shè)計(jì)一個(gè)Cron

關(guān)于Cron,調(diào)用者所有的操作與系統(tǒng)執(zhí)行對應(yīng)的任務(wù)之間是異步的。因此,對于調(diào)用者來說,系統(tǒng)用例如下:

更進(jìn)一步,可以查看下Cron提供的API:

type Cron struct {
	// Has unexported fields.
}
    Cron keeps track of any number of entries, invoking the associated func as
    specified by the schedule. It may be started, stopped, and the entries may
    be inspected while running.

func New(opts ...Option) *Cron
func (c *Cron) AddFunc(spec string, cmd func()) (EntryID, error)
func (c *Cron) AddJob(spec string, cmd Job) (EntryID, error)
func (c *Cron) Entries() []Entry
func (c *Cron) Entry(id EntryID) Entry
func (c *Cron) Location() *time.Location
func (c *Cron) Remove(id EntryID)
func (c *Cron) Run()
func (c *Cron) Schedule(schedule Schedule, cmd Job) EntryID
func (c *Cron) Start()
func (c *Cron) Stop() context.Context

調(diào)用者添加完所有任務(wù)之后,系統(tǒng)的處理流程如下(從后臺任務(wù)的角度看):

上述就是后臺任務(wù)的流程,簡化后的代碼如下:

func (c *Cron) run() {
	// Figure out the next activation times for each entry.
	now := c.now()

	for {
		// Determine the next entry to run.
		// 將所有任務(wù),按照下一次運(yùn)行時(shí)間排序
    sort.Sort(byTime(c.entries))
    
		for {
			select {
			case now = <-timer.C:
				now = now.In(c.location)
				c.logger.Info("wake", "now", now)

				// Run every entry whose next time was less than now
				for _, e := range c.entries {
					if e.Next.After(now) || e.Next.IsZero() {
						break
					}
					c.startJob(e.WrappedJob)
					e.Prev = e.Next
					e.Next = e.Schedule.Next(now)
					c.logger.Info("run", "now", now, "entry", e.ID, "next", e.Next)
				}

    	// 新增一個(gè)任務(wù)
			case newEntry := <-c.add:
      	....
        // 添加任務(wù)到數(shù)組容器
        
    	 // 獲取當(dāng)前時(shí)刻,Cron里面所有的定時(shí)任務(wù)
			case replyChan := <-c.snapshot:
				replyChan <- c.entrySnapshot()
				continue

    	// 停止Cron
			case <-c.stop:
      	...
				return

    	// 移除某個(gè)定時(shí)任務(wù)
			case id := <-c.remove:
        ....
				c.removeEntry(id)

			}

			break
		}
	}
}

四、學(xué)習(xí)點(diǎn)

1. 通過channel傳輸快照

func (c *Cron) Entries() []Entry {
    c.runningMu.Lock()
    defer c.runningMu.Unlock()

    // 如果Cron,正在運(yùn)行,那么返回一個(gè)通道
    if c.running {
        replyChan := make(chan []Entry, 1)
        c.snapshot <- replyChan
        return <-replyChan
    }

    // 如果Cron,已經(jīng)結(jié)束了,直接返回所有Entry
    return c.entrySnapshot()
}

這種寫法特別有意思。當(dāng)調(diào)用者想查看當(dāng)前系統(tǒng)所有的任務(wù)時(shí),系統(tǒng)返回的是一個(gè)通道,接著在通道中返回所有的數(shù)據(jù)。具體時(shí)序圖如下所示:

下面這個(gè)架構(gòu)圖畫的不是很好,畫都畫了就放這吧。

2. 匹配規(guī)則

讀到cron這個(gè)項(xiàng)目,你是否有這樣的疑問?cron后臺任務(wù)根據(jù)調(diào)用給定的規(guī)則,如何執(zhí)行任務(wù)的呢?比如"* * * * 1 *",系統(tǒng)是如何知道每年的第一個(gè)月執(zhí)行相應(yīng)的任務(wù)呢?下面代碼,以月份為例。

程序的大致流程:

  • 將月份規(guī)則轉(zhuǎn)化為二進(jìn)制數(shù)值;
  • 通過當(dāng)前時(shí)間不斷+1,直到匹配規(guī)則月份;

這里主要借助下面這個(gè)函數(shù):

func getBits(min, max, step uint) uint64 {
    var bits uint64

    // If step is 1, use shifts.
    if step == 1 {
        return ^(math.MaxUint64 << (max + 1)) & (math.MaxUint64 << min)
    }

    // Else, use a simple loop.
    for i := min; i <= max; i += step {
        bits |= 1 << i
    }
    return bits
}

func TestGetBits(t *testing.T) {
    res := getBits(1, 3, 1)

    fmt.Printf("%d 的二進(jìn)制表示是 %b\n", res, res)
}

3. 實(shí)現(xiàn)接口的函數(shù)

// Job is an interface for submitted cron jobs.
type Job interface {
    Run()
}

type FuncJob func()

func (f FuncJob) Run() { f() }

上述代碼定義Job接口、FuncJob類型,并且函數(shù)類型實(shí)現(xiàn)了Job接口。這種寫法很常見,比如http.HandleFunc。這樣寫的好處,能夠?qū)⒁粋€(gè)函數(shù)強(qiáng)轉(zhuǎn)之后直接丟到接口參數(shù)中,具體轉(zhuǎn)化流程如下:

func() 類型函數(shù) -- 強(qiáng)轉(zhuǎn):FuncJob(func()) -- FuncJob -- 可以丟進(jìn)Job接口中;

到此這篇關(guān)于Go語言定時(shí)任務(wù)cron的設(shè)計(jì)與使用的文章就介紹到這了,更多相關(guān)Go定時(shí)任務(wù)cron內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Golang HTML 模板使用指南示例詳解

    Golang HTML 模板使用指南示例詳解

    本文詳細(xì)介紹了Golang HTML模板的使用方法,包括基礎(chǔ)模板、高級模板、完整應(yīng)用示例、CSS樣式、JavaScript交互等,文章強(qiáng)調(diào)了模板組織、代碼復(fù)用、語義化HTML、響應(yīng)式設(shè)計(jì)、性能優(yōu)化等最佳實(shí)踐,感興趣的朋友跟隨小編一起看看吧
    2025-01-01
  • Golang流程控制語句的具體使用

    Golang流程控制語句的具體使用

    在編寫程序時(shí),流程控制是必不可少的一部分,本文主要介紹了Golang流程控制語句的具體使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-05-05
  • golang利用pprof與go-torch如何做性能分析

    golang利用pprof與go-torch如何做性能分析

    這篇文章主要給大家介紹了關(guān)于golang利用pprof與go-torch如何做性能分析的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-07-07
  • 源碼剖析Golang如何fork一個(gè)進(jìn)程

    源碼剖析Golang如何fork一個(gè)進(jìn)程

    創(chuàng)建一個(gè)新進(jìn)程分為兩個(gè)步驟,一個(gè)是fork系統(tǒng)調(diào)用,一個(gè)是execve?系統(tǒng)調(diào)用,本文將從源碼的角度帶大家剖析一下Golang是如何fork一個(gè)進(jìn)程的
    2023-06-06
  • Golang網(wǎng)絡(luò)模型netpoll源碼解析(具體流程)

    Golang網(wǎng)絡(luò)模型netpoll源碼解析(具體流程)

    本文介紹了Golang的網(wǎng)絡(luò)模型netpoll的實(shí)現(xiàn)原理,本文將從為什么需要使用netpoll模型,以及netpoll的具體流程實(shí)現(xiàn)兩個(gè)主要角度來展開學(xué)習(xí),感興趣的朋友跟隨小編一起看看吧
    2024-11-11
  • Golang中切片長度和容量的區(qū)別示例詳解

    Golang中切片長度和容量的區(qū)別示例詳解

    切片長度與容量在Go中很常見,切片長度是切片中可用元素的數(shù)量,而切片容量是從切片中第一個(gè)元素開始計(jì)算的底層數(shù)組中的元素?cái)?shù)量,這篇文章主要給大家介紹了關(guān)于Golang中切片長度和容量區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2024-01-01
  • Go外部依賴包從vendor,$GOPATH和$GOPATH/pkg/mod查找順序

    Go外部依賴包從vendor,$GOPATH和$GOPATH/pkg/mod查找順序

    這篇文章主要介紹了Go外部依賴包vendor,$GOPATH和$GOPATH/pkg/mod下查找順序,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Golang TCP粘包拆包問題的解決方法

    Golang TCP粘包拆包問題的解決方法

    這篇文章主要給大家介紹了Golang TCP粘包拆包問題的解決方法,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Golang具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • Golang中切片的用法與本質(zhì)詳解

    Golang中切片的用法與本質(zhì)詳解

    Go的切片類型為處理同類型數(shù)據(jù)序列提供一個(gè)方便而高效的方式,下面這篇文章就來給大家介紹了關(guān)于Golang中切片的用法與本質(zhì)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2018-07-07
  • golang框架中跨服務(wù)的最佳通信協(xié)議和工具

    golang框架中跨服務(wù)的最佳通信協(xié)議和工具

    在 go 框架中實(shí)現(xiàn)跨服務(wù)通信的最佳實(shí)踐包括使用 grpc(適用于低延遲高吞吐量)、http 客戶端(適用于 restful api)和消息隊(duì)列(適用于異步解耦通信),在選擇通信方式時(shí),應(yīng)考慮服務(wù)交互模式、性能要求和部署環(huán)境等因素
    2024-06-06

最新評論