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

Golang定時(shí)任務(wù)框架GoCron的源碼分析

 更新時(shí)間:2025年03月04日 09:46:26   作者:AlfredChaos  
本文主要介紹了Golang定時(shí)任務(wù)框架GoCron的源碼分析,原生的gocron存在一些問(wèn)題,如任務(wù)列表維護(hù)不當(dāng)、并發(fā)操作不可預(yù)測(cè)等,經(jīng)過(guò)改進(jìn)的gocron解決了這些問(wèn)題,感興趣的可以了解一下

背景說(shuō)明

最近工作上有個(gè)開(kāi)發(fā)定時(shí)任務(wù)的需求,調(diào)研一下后發(fā)現(xiàn)Golang并沒(méi)有十分完善的定時(shí)任務(wù)庫(kù)。
整理我這邊的需求如下:

  • 支持啟動(dòng)僅定時(shí)執(zhí)行一次的任務(wù);
  • 任務(wù)在執(zhí)行之前可以完成撤銷(xiāo);
  • 服務(wù)重啟之后,未完成的定時(shí)任務(wù)需要允許重新調(diào)度;

顯然,現(xiàn)成的cron庫(kù)無(wú)法滿足我的需求。限定于工期,最終自己實(shí)現(xiàn)了一個(gè)粗糙的事件驅(qū)動(dòng)定時(shí)器。

自己實(shí)現(xiàn)的事件驅(qū)動(dòng)定時(shí)器

但這個(gè)事件驅(qū)動(dòng)定時(shí)器具有以下的缺點(diǎn):

  • 事件訂閱/通知機(jī)制不成熟
  • 無(wú)法適用于更靈活的場(chǎng)景,例如多節(jié)點(diǎn)的分布式任務(wù)調(diào)度執(zhí)行
  • 模塊之間的職責(zé)不清晰,例如其實(shí)Timer模塊是Scheduler調(diào)度器的一部分,Event定時(shí)器相關(guān)的部分也是Scheduler調(diào)度器的一部分,而Executor執(zhí)行模塊也存在任務(wù)調(diào)度的功能,實(shí)際上它只需要負(fù)責(zé)完成調(diào)度器交給它的任務(wù)就好
  • 沒(méi)有設(shè)計(jì)任務(wù)調(diào)度池,也就是但凡新建計(jì)劃任務(wù),就會(huì)在后臺(tái)啟動(dòng)一個(gè)協(xié)程持續(xù)監(jiān)聽(tīng);一旦任務(wù)數(shù)量太多,后臺(tái)停留的協(xié)程會(huì)越來(lái)越多,進(jìn)程總的消耗就會(huì)變得非常夸張,非常可怕
  • 任務(wù)調(diào)度時(shí)不存在優(yōu)先級(jí)的概念,假如相同時(shí)間內(nèi)有多個(gè)任務(wù)同時(shí)執(zhí)行,哪個(gè)任務(wù)被優(yōu)先調(diào)度完全取決于GMP的系統(tǒng)調(diào)度

綜上,我需要著重考察現(xiàn)有的Golang任務(wù)調(diào)度框架,對(duì)任務(wù)定時(shí)器進(jìn)行重新設(shè)計(jì)。

GoCron任務(wù)調(diào)度庫(kù)

https://github.com/jasonlvhit/gocron

調(diào)用實(shí)例

package main

import (
	"fmt"
	"time"

	"github.com/jasonlvhit/gocron"
)

func task() {
	fmt.Println("I am running task.")
}

func taskWithParams(a int, b string) {
	fmt.Println(a, b)
}

func main() {
	// Do jobs without params
	gocron.Every(1).Second().Do(task)
	gocron.Every(2).Seconds().Do(task)
	gocron.Every(1).Minute().Do(task)
	gocron.Every(2).Minutes().Do(task)
	gocron.Every(1).Hour().Do(task)
	gocron.Every(2).Hours().Do(task)
	gocron.Every(1).Day().Do(task)
	gocron.Every(2).Days().Do(task)
	gocron.Every(1).Week().Do(task)
	gocron.Every(2).Weeks().Do(task)

	// Do jobs with params
	gocron.Every(1).Second().Do(taskWithParams, 1, "hello")

	// Do jobs on specific weekday
	gocron.Every(1).Monday().Do(task)
	gocron.Every(1).Thursday().Do(task)

	// Do a job at a specific time - 'hour:min:sec' - seconds optional
	gocron.Every(1).Day().At("10:30").Do(task)
	gocron.Every(1).Monday().At("18:30").Do(task)
	gocron.Every(1).Tuesday().At("18:30:59").Do(task)

	// Begin job immediately upon start
	gocron.Every(1).Hour().From(gocron.NextTick()).Do(task)

	// Begin job at a specific date/time
	t := time.Date(2019, time.November, 10, 15, 0, 0, 0, time.Local)
	gocron.Every(1).Hour().From(&t).Do(task)

	// NextRun gets the next running time
	_, time := gocron.NextRun()
	fmt.Println(time)

	// Remove a specific job
	gocron.Remove(task)

	// Clear all scheduled jobs
	gocron.Clear()

	// Start all the pending jobs
	<- gocron.Start()

	// also, you can create a new scheduler
	// to run two schedulers concurrently
	s := gocron.NewScheduler()
	s.Every(3).Seconds().Do(task)
	<- s.Start()
}

項(xiàng)目分析

這個(gè)工具庫(kù)僅有三個(gè)文件:

gocron工具庫(kù)

代碼主要分為job和scheduler兩個(gè)文件,gocron僅放置了回調(diào)方法和公共方法。項(xiàng)目整體架構(gòu)如下:

gocron項(xiàng)目架構(gòu)

gocron通過(guò)scheduler維護(hù)一個(gè)job列表,指定MAXJOBNUM最大工作隊(duì)列,限制可執(zhí)行的工作數(shù)大小。

// gocron/scheduler.go
// Scheduler struct, the only data member is the list of jobs.
// - implements the sort.Interface{} for sorting jobs, by the time nextRun
type Scheduler struct {
	jobs [MAXJOBNUM]*Job // Array store jobs
	size int             // Size of jobs which jobs holding.
	loc  *time.Location  // Location to use when scheduling jobs with specified times
}

這里需要更正一下,并不是全局列表,僅僅只是跟隨調(diào)度器的生命周期。實(shí)際上,代碼確實(shí)存在全局的默認(rèn)調(diào)度器:

var (
	defaultScheduler = NewScheduler()
)

因此,可以直接調(diào)用。當(dāng)然也支持實(shí)例化自己的調(diào)度器:

s := gocron.NewScheduler()
s.Every(3).Seconds().Do(task)
<- s.Start()

gocron是典型的鏈?zhǔn)秸{(diào)用,scheduler對(duì)象通過(guò)返回job對(duì)象,完成job對(duì)象的封裝操作之后,加入調(diào)度器內(nèi)部的jobs列表,再通過(guò)Start方法啟動(dòng)調(diào)度器監(jiān)控協(xié)程,輪詢(xún)列表中的jobs,一旦找到可執(zhí)行的任務(wù),就會(huì)啟動(dòng)協(xié)程運(yùn)行job的Func對(duì)象。

// Job struct keeping information about job
type Job struct {
	interval uint64                   // pause interval * unit between runs
	jobFunc  string                   // the job jobFunc to run, func[jobFunc]
	//......
	funcs    map[string]interface{}   // Map for the function task store
	fparams  map[string][]interface{} // Map for function and  params of function
	//......
}

funcs維護(hù)一個(gè)map,緩存funcName到func的映射關(guān)系。具體封裝在Do方法:

// gocron/job.go
// func (j *Job) Do(jobFun interface{}, params ...interface{}) error
fname := getFunctionName(jobFun)
j.funcs[fname] = jobFun
j.fparams[fname] = params
j.jobFunc = fname

在執(zhí)行任務(wù)時(shí),通過(guò)反射回調(diào)func:

// gocron/job.go
// func (j *Job) run() ([]reflect.Value, error)
result, err := callJobFuncWithParams(j.funcs[j.jobFunc], j.fparams[j.jobFunc])
if err != nil {
	return nil, err
}

// gocron/gocron.go
func callJobFuncWithParams(jobFunc interface{}, params []interface{}) ([]reflect.Value, error) {
	f := reflect.ValueOf(jobFunc)
	if len(params) != f.Type().NumIn() {
		return nil, ErrParamsNotAdapted
	}
	in := make([]reflect.Value, len(params))
	for k, param := range params {
		in[k] = reflect.ValueOf(param)
	}
	return f.Call(in), nil
}

啟動(dòng)調(diào)度器時(shí),啟動(dòng)監(jiān)控協(xié)程:

// Start all the pending jobs
// Add seconds ticker
func (s *Scheduler) Start() chan bool {
	stopped := make(chan bool, 1)
	// ticker每秒產(chǎn)生一個(gè)信號(hào)
	ticker := time.NewTicker(1 * time.Second)

	go func() {
		for {
			// select選擇器阻塞
			// case接收到信號(hào)則執(zhí)行
			// 同時(shí)接收到多個(gè)信號(hào)則隨機(jī)選擇一個(gè)執(zhí)行
			select {
			// ticker每秒產(chǎn)生一次信號(hào)
			// RunPending輪詢(xún)jobs列表,尋找到了時(shí)間可執(zhí)行的任務(wù)
			case <-ticker.C:
				s.RunPending()
			// stopped接收到停止信號(hào),退出調(diào)度器協(xié)程
			case <-stopped:
				ticker.Stop()
				return
			}
		}
	}()

	return stopped
}

一個(gè)調(diào)度器一個(gè)協(xié)程,通過(guò)統(tǒng)一的調(diào)度協(xié)程去監(jiān)控調(diào)度器任務(wù)列表內(nèi)的任務(wù)。

// RunPending runs all the jobs that are scheduled to run.
func (s *Scheduler) RunPending() {
	// 輪詢(xún)jobs列表,找到到時(shí)間可執(zhí)行的任務(wù),創(chuàng)建可執(zhí)行任務(wù)列表
	runnableJobs, n := s.getRunnableJobs()

	if n != 0 {
		for i := 0; i < n; i++ {
			// 啟動(dòng)協(xié)程運(yùn)行
			go runnableJobs[i].run()
			// 刷新job執(zhí)行信息,等待下一輪調(diào)度
			runnableJobs[i].lastRun = time.Now()
			runnableJobs[i].scheduleNextRun()
		}
	}
}

綜合分析

綜上,gocron有如下好處:

  • 鏈?zhǔn)秸{(diào)用簡(jiǎn)單易用
  • scheduler和job職責(zé)清晰,項(xiàng)目架構(gòu)非常容易理解
  • 調(diào)度器一鍵啟動(dòng)協(xié)程監(jiān)控,只有到了時(shí)間可執(zhí)行的任務(wù)才會(huì)被加入到runablejobs列表,大大減少了進(jìn)程中協(xié)程的數(shù)量,減少資源消耗
  • 調(diào)度器維護(hù)的待執(zhí)行任務(wù)池,存在預(yù)設(shè)的容量大小,限定了同時(shí)可執(zhí)行的最大任務(wù)數(shù)量,不會(huì)導(dǎo)致超量

但它的缺陷也同樣明顯:

  • 當(dāng)不同的線程同時(shí)對(duì)同一個(gè)調(diào)度器進(jìn)行操作,對(duì)任務(wù)列表產(chǎn)生的影響是不可預(yù)知的。因此這個(gè)框架下,最好是每個(gè)client維護(hù)自己的scheduler對(duì)象
  • 雖然調(diào)度器維護(hù)一個(gè)jobs列表,但如果超過(guò)列表設(shè)定容量的任務(wù)便無(wú)法等待執(zhí)行了……這一點(diǎn)gocron并沒(méi)有理睬
  • 幾乎每秒,為了找到可執(zhí)行的任務(wù)去構(gòu)建runablejobs列表,都會(huì)輪詢(xún)一次任務(wù)列表。為了追求結(jié)果的一致,它會(huì)對(duì)jobs進(jìn)行排序,雖然Golang編譯器對(duì)內(nèi)置的sort方法進(jìn)行了優(yōu)化,會(huì)選舉最快的方式對(duì)數(shù)據(jù)進(jìn)行處理,但依然存在消耗
  • 依然是內(nèi)存操作,服務(wù)重啟任務(wù)列表就不存在了。也沒(méi)有考慮到多節(jié)點(diǎn)的場(chǎng)景。

新的GoCron分析

https://github.com/go-co-op/gocron
原gocron的作者居然住進(jìn)ICU了,管理員說(shuō)截止至2020年3月依然無(wú)法聯(lián)系上他。愿他身體安康……gocron被fork后有了新的發(fā)展,趕緊扒下來(lái)學(xué)習(xí)一下

新的gocron新增了很多內(nèi)容,依然圍繞著Scheduler和Job進(jìn)行鏈?zhǔn)讲僮鳎略隽薳xecutor模塊。executor僅負(fù)責(zé)執(zhí)行Scheduler調(diào)度過(guò)來(lái)的任務(wù)。

項(xiàng)目架構(gòu)

下面是項(xiàng)目README文檔里公開(kāi)的架構(gòu)圖:

?。〈罄袀儺?huà)圖能不能認(rèn)真一點(diǎn)啊雖然已經(jīng)非常生動(dòng)形象了

新功能

新版gocron支持了cron格式的語(yǔ)法

// cron expressions supported
s.Cron("*/1 * * * *").Do(task) // every minute

新增了異步和阻塞模式的兩種調(diào)度方式

// you can start running the scheduler in two different ways:
// starts the scheduler asynchronously
s.StartAsync()
// starts the scheduler and blocks current execution path
s.StartBlocking()

通過(guò)設(shè)置信號(hào)量限制可同時(shí)運(yùn)行的任務(wù)數(shù)量

// gocron/scheduler.go
// SetMaxConcurrentJobs limits how many jobs can be running at the same time.
// This is useful when running resource intensive jobs and a precise start time is not critical.
func (s *Scheduler) SetMaxConcurrentJobs(n int, mode limitMode) {
	// 通過(guò)對(duì)n的配置修改并發(fā)任務(wù)數(shù)的大小
	s.executor.maxRunningJobs = semaphore.NewWeighted(int64(n))
	// limitMode即當(dāng)可執(zhí)行任務(wù)達(dá)到最大并發(fā)量時(shí),應(yīng)該如何處理的邏輯
	// RescheduleMode:跳過(guò)本次執(zhí)行,等待下一次調(diào)度
	// WaitMode:持續(xù)等待,知道可執(zhí)行隊(duì)列空出。但,由于等待的任務(wù)數(shù)積累,可能導(dǎo)致不可預(yù)知的后果,某些任務(wù)可能一直等不到執(zhí)行
	s.executor.limitMode = mode
}

// gocron/executor.go
// 通過(guò)信號(hào)量的方式從最大數(shù)量中取一位
// 若通過(guò),下一步可以執(zhí)行函數(shù)
if e.maxRunningJobs != nil {
	if !e.maxRunningJobs.TryAcquire(1) {

		switch e.limitMode {
		case RescheduleMode:
			return
		case WaitMode:
			select {
			case <-stopCtx.Done():
				return
			case <-f.ctx.Done():
				return
			default:
			}

			if err := e.maxRunningJobs.Acquire(f.ctx, 1); err != nil {
				break

			}
		}
	}

	defer e.maxRunningJobs.Release(1)
}

gocron支持指定Job以單例模式運(yùn)行。通過(guò)siglefilght工具庫(kù)保證當(dāng)前僅有一個(gè)可運(yùn)行的Job

// gocron/job.go
// SingletonMode prevents a new job from starting if the prior job has not yet
// completed it's run
// Note: If a job is added to a running scheduler and this method is then used
// you may see the job run overrun itself as job is scheduled immediately
// by default upon being added to the scheduler. It is recommended to use the
// SingletonMode() func on the scheduler chain when scheduling the job.
func (j *Job) SingletonMode() {
	j.mu.Lock()
	defer j.mu.Unlock()
	j.runConfig.mode = singletonMode
	j.jobFunction.limiter = &singleflight.Group{}
}

// gocron/executor.go
switch f.runConfig.mode {
case defaultMode:
	runJob()
case singletonMode:
	// limiter是singlefilght對(duì)象,Do方法內(nèi)僅會(huì)執(zhí)行一次,保證一次只運(yùn)行一個(gè)任務(wù)
	_, _, _ = f.limiter.Do("main", func() (interface{}, error) {
		select {
		case <-stopCtx.Done():
			return nil, nil
		case <-f.ctx.Done():
			return nil, nil
		default:
		}
		runJob()
		return nil, nil
	})
}

gocron主要數(shù)據(jù)結(jié)構(gòu)

主要分為schduler調(diào)度器,job任務(wù),以及executor執(zhí)行器對(duì)象

主要數(shù)據(jù)結(jié)構(gòu)

追蹤一下調(diào)用鏈的工作流程:

  • 初始化一個(gè)Scheduler;新版gocron似乎更鼓勵(lì)用戶使用自己的scheduler,而不是如同老版一樣維護(hù)一個(gè)默認(rèn)的全局調(diào)度器
func NewScheduler(loc *time.Location) *Scheduler {
	//	這時(shí)已經(jīng)將executor同步初始化完畢
	// scheduler和executor是一對(duì)一的關(guān)系
	executor := newExecutor()

	return &Scheduler{
		jobs:       make([]*Job, 0),
		location:   loc,
		running:    false,
		time:       &trueTime{},
		executor:   &executor,
		tagsUnique: false,
		timer:      afterFunc,
	}
}
  • Every方法初始化一個(gè)Job,如果scheduler已經(jīng)啟動(dòng),即任務(wù)列表中已經(jīng)存在一個(gè)等待封裝的Job,那么直接取出相應(yīng)的Job
if s.updateJob || s.jobCreated {
	job = s.getCurrentJob()
}

接下來(lái)確定Job的運(yùn)行周期,并加入到任務(wù)列表

s.setJobs(append(s.Jobs(), job))

Every方法返回了新增Job的scheduler,此時(shí)scheduler的任務(wù)隊(duì)列中存在一個(gè)Job就緒,等待下一步調(diào)度。

  • Do方法帶著回調(diào)的函數(shù)和對(duì)應(yīng)的參數(shù)開(kāi)始執(zhí)行,它從當(dāng)前的scheduler中取出一個(gè)就緒的Job,進(jìn)行最后的判斷,如果Job不合格,那么將它從任務(wù)隊(duì)列中移除,并返回報(bào)錯(cuò)
if job.error != nil {
	// delete the job from the scheduler as this job
	// cannot be executed
	s.RemoveByReference(job)
	return nil, job.error
}
// 還有很多判斷條件,這里不一一列舉

將Do方法將要執(zhí)行的函數(shù)封裝進(jìn)Job。接下來(lái)判斷schduler是否啟動(dòng):如之前gocron一樣,scheduler也是通過(guò)協(xié)程監(jiān)聽(tīng)并執(zhí)行啟動(dòng)任務(wù)協(xié)程的工作。

之前的scheduler,默認(rèn)啟動(dòng)一個(gè)ticker,每秒去排序并輪詢(xún)?nèi)蝿?wù)隊(duì)列,從中取出滿足條件的任務(wù)開(kāi)始執(zhí)行,效率非常低。而現(xiàn)在的改進(jìn)是:scheduler啟動(dòng)監(jiān)聽(tīng)協(xié)程后;不是以輪詢(xún)而是以通知的方式,從channel中獲取Job的Function,再啟動(dòng)協(xié)程去執(zhí)行。

在這樣的前提下,scheduler監(jiān)聽(tīng)協(xié)程什么時(shí)候啟動(dòng)是位置的。此處添加一個(gè)判斷,當(dāng)scheduler啟動(dòng)時(shí),同時(shí)啟動(dòng)runContinuous去完成Job的最后一步操作。若是scheduler沒(méi)有啟動(dòng),那么直接返回,等待scheduler啟動(dòng)后再完成操作。

// we should not schedule if not running since we can't foresee how long it will take for the scheduler to start
if s.IsRunning() {
	s.runContinuous(job)
}

通過(guò)這樣的設(shè)計(jì),在最終啟動(dòng)scheduler前后,都可以以動(dòng)態(tài)的方式添加/移除任務(wù)。

  • scheduler提供了兩種啟動(dòng)schduler的模式:異步和阻塞(也就是同步啦)
// StartAsync starts all jobs without blocking the current thread
func (s *Scheduler) StartAsync() {
	if !s.IsRunning() {
		s.start()
	}
}

// StartBlocking starts all jobs and blocks the current thread.
// This blocking method can be stopped with Stop() from a separate goroutine.
func (s *Scheduler) StartBlocking() {
	s.StartAsync()
	s.startBlockingStopChanMutex.Lock()
	s.startBlockingStopChan = make(chan struct{}, 1)
	s.startBlockingStopChanMutex.Unlock()
	<-s.startBlockingStopChan
}

一般情況下,我們通過(guò)異步模式,啟動(dòng)對(duì)所有任務(wù)的監(jiān)控

// start starts the scheduler, scheduling and running jobs
func (s *Scheduler) start() {
	// 啟動(dòng)監(jiān)聽(tīng)協(xié)程,select選擇器配合channel阻塞
	// 直到Job準(zhǔn)備執(zhí)行發(fā)送通知
	go s.executor.start()
	// 將scheduler置位為running
	s.setRunning(true)
	// 遍歷所有任務(wù),以遞歸的方式監(jiān)控起來(lái)
	s.runJobs(s.Jobs())
}

比較有意思的是這個(gè)部分:

func (s *Scheduler) runJobs(jobs []*Job) {
	for _, job := range jobs {
		// 這個(gè)函數(shù)是一個(gè)遞歸調(diào)用
		// 這里對(duì)所有Job都以遞歸的方式監(jiān)聽(tīng)著
		s.runContinuous(job)
	}
}

// 這是runContinuous的部分代碼
job.setTimer(s.timer(nextRun, func() {
	if !next.dateTime.IsZero() {
		for {
			n := s.now().UnixNano() - next.dateTime.UnixNano()
			// 某個(gè)任務(wù)滿足執(zhí)行條件了,退出循環(huán)
			if n >= 0 {
				break
			}
			s.time.Sleep(time.Duration(n))
		}
	}
	// 遞歸執(zhí)行本方法
	// runContinuous會(huì)判斷當(dāng)前Job是否可執(zhí)行
	// 若不則退出,若可以則將Job設(shè)置為立即執(zhí)行,并刷新執(zhí)行時(shí)間
	// 若Job“立即執(zhí)行”的標(biāo)志已經(jīng)置位,直接調(diào)用run發(fā)送通知給監(jiān)聽(tīng)協(xié)程
	s.runContinuous(job)
}))

這樣的設(shè)計(jì)太優(yōu)雅了,大佬們的奇思妙想啊~

  • 最后是executor的執(zhí)行,前面已經(jīng)提到過(guò)。通過(guò)select接收channel通知的形式執(zhí)行下去,核心方法是這個(gè):
runJob := func() {
	f.incrementRunState()
	callJobFunc(f.eventListeners.onBeforeJobExecution)
	callJobFuncWithParams(f.function, f.parameters)
	callJobFunc(f.eventListeners.onAfterJobExecution)
	f.decrementRunState()
}

eventListeners封裝了兩個(gè)接口,用以在執(zhí)行任務(wù)和完成任務(wù)后發(fā)送給用戶事件通知。

綜合分析

gocron進(jìn)行了不少方面的優(yōu)化:

  • 在任務(wù)列表的維護(hù)上,可加入調(diào)度的任務(wù)數(shù)不再限定為某個(gè)值,而是以切片的方式自動(dòng)增長(zhǎng)。但最終能夠并行執(zhí)行的任務(wù)數(shù)卻通過(guò)信號(hào)量多方式加以控制;
  • 不再周期性地輪詢(xún)?nèi)蝿?wù)列表,以期待獲得可運(yùn)行的任務(wù);而是通過(guò)更巧妙的方式,任務(wù)遞歸監(jiān)聽(tīng),一旦發(fā)現(xiàn)可執(zhí)行的任務(wù),就自行通知scheduler,完成調(diào)度;
  • 具備更豐富的語(yǔ)法和模式,用戶可以根據(jù)場(chǎng)景自行選擇;調(diào)度器同時(shí)支持異步及同步調(diào)用,而Job也支持周期性輪詢(xún)和單點(diǎn)任務(wù);
  • scheduler內(nèi)加鎖了,對(duì)Jobs列表的操作都會(huì)加上讀寫(xiě)鎖,一些其它的參數(shù)也擁有自己的鎖。這使得scheduler具備線程安全性,但某種程度上影響了對(duì)Jobs隊(duì)列的操作??紤]到gocron不再鼓勵(lì)使用全局Scheduler,而是每個(gè)client維護(hù)自己的Scheduler,那么被鎖影響的場(chǎng)景會(huì)進(jìn)一步減少,與最終優(yōu)化獲得的性能提升相比,都是值得的。

最后

最后的最后,gocron依然無(wú)法滿足我當(dāng)前的需求,但已經(jīng)不妨礙我對(duì)源碼進(jìn)行下一步的改造:

  • 我需要對(duì)Job進(jìn)行上層的封裝,并將要調(diào)用的方法和參數(shù)序列化后存入數(shù)據(jù)庫(kù),直到服務(wù)重啟時(shí),能夠找到未完成的任務(wù)加載進(jìn)scheduler重新執(zhí)行
  • 我的計(jì)劃任務(wù)只需要執(zhí)行一次,而無(wú)須重復(fù)執(zhí)行,這一點(diǎn)已經(jīng)有SingletonMode保證
  • 我需要改造gocron,讓它能夠支持在某個(gè)時(shí)間范圍內(nèi)調(diào)度任務(wù)

到此這篇關(guān)于Golang定時(shí)任務(wù)框架GoCron的源碼分析的文章就介紹到這了,更多相關(guān)Golang定時(shí)任務(wù)框架GoCron內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

  • 一文搞懂Golang中iota的用法和原理

    一文搞懂Golang中iota的用法和原理

    我們知道iota是go語(yǔ)言的常量計(jì)數(shù)器,本文嘗試全面總結(jié)其使用用法以及其實(shí)現(xiàn)原理,需要的朋友可以參考以下內(nèi)容,希望對(duì)大家有所幫助
    2022-08-08
  • Go通過(guò)SJSON實(shí)現(xiàn)動(dòng)態(tài)修改JSON

    Go通過(guò)SJSON實(shí)現(xiàn)動(dòng)態(tài)修改JSON

    在Go語(yǔ)言 json 處理領(lǐng)域,在 json 數(shù)據(jù)處理中,讀取與修改是兩個(gè)核心需求,本文我們就來(lái)看看如何使用SJSON進(jìn)行動(dòng)態(tài)修改JSON吧,有需要的小伙伴可以了解下
    2025-03-03
  • golang之反射和斷言的具體使用

    golang之反射和斷言的具體使用

    這篇文章主要介紹了golang之反射和斷言的具體使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • 詳解 Go 語(yǔ)言中 Map 類(lèi)型和 Slice 類(lèi)型的傳遞

    詳解 Go 語(yǔ)言中 Map 類(lèi)型和 Slice 類(lèi)型的傳遞

    這篇文章主要介紹了詳解 Go 語(yǔ)言中 Map 類(lèi)型和 Slice 類(lèi)型的傳遞的相關(guān)資料,需要的朋友可以參考下
    2017-09-09
  • Beego AutoRouter工作原理解析

    Beego AutoRouter工作原理解析

    這篇文章主要為大家介紹了Beego AutoRouter工作原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • GO語(yǔ)言原生實(shí)現(xiàn)文件上傳功能

    GO語(yǔ)言原生實(shí)現(xiàn)文件上傳功能

    這篇文章主要為大家詳細(xì)介紹了GO語(yǔ)言原生實(shí)現(xiàn)文件上傳功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • golang調(diào)用shell命令(實(shí)時(shí)輸出,終止)

    golang調(diào)用shell命令(實(shí)時(shí)輸出,終止)

    本文主要介紹了golang調(diào)用shell命令(實(shí)時(shí)輸出,終止),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • Golang實(shí)現(xiàn)http重定向https

    Golang實(shí)現(xiàn)http重定向https

    這篇文章介紹了Golang實(shí)現(xiàn)http重定向https的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07
  • 掌握Golang中的select語(yǔ)句實(shí)現(xiàn)并發(fā)編程

    掌握Golang中的select語(yǔ)句實(shí)現(xiàn)并發(fā)編程

    Golang中的select語(yǔ)句用于在多個(gè)通道間選擇可讀或可寫(xiě)的操作,并阻塞等待其中一個(gè)通道進(jìn)行操作??梢杂糜趯?shí)現(xiàn)超時(shí)控制、取消和中斷操作等。同時(shí),select語(yǔ)句支持default分支,用于在沒(méi)有任何通道可操作時(shí)執(zhí)行默認(rèn)操作
    2023-04-04
  • golang字符串匹配算法解讀

    golang字符串匹配算法解讀

    文章介紹了字符串匹配算法的原理,特別是Knuth-Morris-Pratt(KMP)算法,該算法通過(guò)構(gòu)建模式串的前綴表來(lái)減少匹配時(shí)的不必要的字符比較,從而提高效率,在Golang中實(shí)現(xiàn)KMP算法時(shí),需要構(gòu)建前綴表并在文本串中進(jìn)行匹配
    2025-02-02

最新評(píng)論