在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

解析Golang定時(shí)任務(wù)庫gron設(shè)計(jì)和原理

Linux愛好者 ? 來源:Linux愛好者 ? 作者:Linux愛好者 ? 2022-12-15 13:57 ? 次閱讀

從 cron 說起

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

cb04c2ac-7c3c-11ed-8abf-dac502259ad0.jpg

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

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

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

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

gron

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

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

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

$gogetgithub.com/roylee0704/gron

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

packagemain

import(
"fmt"
"time"
"github.com/roylee0704/gron"
)

funcmain(){
c:=gron.New()
c.AddFunc(gron.Every(1*time.Hour),func(){
fmt.Println("runseveryhour.")
})
c.Start()
}

非常簡單,而且即便是在 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 包用來把常見的 time.Duration 封裝起來,這里我們開箱即用。

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

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

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

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é)我們來看看 gron 的實(shí)現(xiàn)原理。

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

  1. 觸發(fā)器。即我們希望這個(gè)任務(wù)在什么時(shí)間點(diǎn),什么周期被觸發(fā);

  2. 任務(wù)。即我們在觸發(fā)之后,希望執(zhí)行的任務(wù),類比到我們上面示例的 fmt.Println。

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

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

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

Cron

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

//Cronprovidesaconvenientinterfaceforschedulingjobsuchastoclean-up
//databaseentryeverymonth.
//
//Cronkeepstrackofanynumberofentries,invokingtheassociatedfuncas
//specifiedbytheschedule.Itmayalsobestarted,stoppedandtheentries
//maybeinspected.
typeCronstruct{
entries[]*Entry
runningbool
addchan*Entry
stopchanstruct{}
}

//NewinstantiatesnewCroninstantc.
funcNew()*Cron{
return&Cron{
stop:make(chanstruct{}),
add:make(chan*Entry),
}
}
  • entries 就是定時(shí)任務(wù)的核心能力,它記錄了一組【定時(shí)任務(wù)】;

  • running 用來標(biāo)識這個(gè) Cron 是否已經(jīng)啟動;

  • add 是一個(gè)channel,用來支持在 Cron 啟動后,新增的【定時(shí)任務(wù)】;

  • stop 同樣是個(gè)channel,注意到是空結(jié)構(gòu)體,用來控制 Cron 的停止。這個(gè)其實(shí)是經(jīng)典寫法了,對日常開發(fā)也有借鑒意義,我們待會兒會好好看一下。

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

Entry

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

//Entryconsistsofascheduleandthejobtobeexecutedonthatschedule.
typeEntrystruct{
ScheduleSchedule
JobJob

//thenexttimethejobwillrun.ThisiszerotimeifCronhasnotbeen
//startedorinvalidschedule.
Nexttime.Time

//thelasttimethejobwasrun.Thisiszerotimeifthejobhasnotbeen
//run.
Prevtime.Time
}

//ScheduleistheinterfacethatwrapsthebasicNextmethod.
//
//Nextdeducesnextoccurringtimebasedontandunderlyingstates.
typeScheduleinterface{
Next(ttime.Time)time.Time
}

//JobistheinterfacethatwrapsthebasicRunmethod.
//
//Runexecutestheunderlyingfunc.
typeJobinterface{
Run()
}
  • Schedule 代表了一個(gè)【觸發(fā)器】,或者說一個(gè)定時(shí)策略。它只包含一個(gè) Next 方法,接受一個(gè)時(shí)間點(diǎn),業(yè)務(wù)要返回下一次觸發(fā)調(diào)動的時(shí)間點(diǎn)。

  • Job 則是對【任務(wù)】的抽象,只需要實(shí)現(xiàn)一個(gè) Run 方法,沒有入?yún)⒊鰠ⅰ?/p>

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

按照時(shí)間排序

//byTimeisahandywrappertochronologicallysortentries.
typebyTime[]*Entry

func(bbyTime)Len()int{returnlen(b)}
func(bbyTime)Swap(i,jint){b[i],b[j]=b[j],b[i]}

//Lessreports`earliest`timeishouldsortbeforej.
//zerotimeisnot`earliest`time.
func(bbyTime)Less(i,jint)bool{

ifb[i].Next.IsZero(){
returnfalse
}
ifb[j].Next.IsZero(){
returntrue
}

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

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

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

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

我們在示例里面出現(xiàn)過調(diào)用 AddFunc() 來加入一個(gè) gron.Every(xxx) 這樣一個(gè)【定時(shí)任務(wù)】。其實(shí)這是給用戶提供的簡單封裝。

//JobFuncisanadaptertoallowtheuseofordinaryfunctionsasgron.Job
//Iffisafunctionwiththeappropriatesignature,JobFunc(f)isahandler
//thatcallsf.
//
//todo:possiblyfuncwithparams?maybenotneeded.
typeJobFuncfunc()

//Runcallsj()
func(jJobFunc)Run(){
j()
}


//AddFuncregisterstheJobfunctionforthegivenSchedule.
func(c*Cron)AddFunc(sSchedule,jfunc()){
c.Add(s,JobFunc(j))
}

//Addappendsschedule,jobtoentries.
//
//ifcroninstantisnotrunning,addingtoentriesistrivial.
//otherwise,topreventdata-race,addsthroughchannel.
func(c*Cron)Add(sSchedule,jJob){

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。并以此來構(gòu)造出一個(gè)定時(shí)任務(wù) Entry。

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

啟動和停止

//Startsignalscroninstantctogetupandrunning.
func(c*Cron)Start(){
c.running=true
goc.run()
}


//Stophaltscroninstantcfromrunning.
func(c*Cron)Stop(){

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

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

  • Start 方法執(zhí)行的時(shí)候會先將 running 變量置為 true,用來標(biāo)識實(shí)例已經(jīng)啟動(啟動前后加入的定時(shí)任務(wù) Entry 處理策略是不同的,所以這里需要標(biāo)識),然后啟動一個(gè) goroutine 來實(shí)際跑啟動的邏輯。

  • Stop 方法則會將 running 置為 false,然后直接往 stop channel 塞一個(gè)空結(jié)構(gòu)體即可。

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

varafter=time.After


//runthescheduler...
//
//Itneedstobeprivateasit'sresponsibleofsynchronizingacritical
//sharedstate:`running`.
func(c*Cron)run(){

vareffectivetime.Time
now:=time.Now().Local()

//tofigurenexttrigtimeforentries,referencedfromnow
for_,e:=rangec.entries{
e.Next=e.Schedule.Next(now)
}

for{
sort.Sort(byTime(c.entries))
iflen(c.entries)>0{
effective=c.entries[0].Next
}else{
effective=now.AddDate(15,0,0)//topreventphantomjobs.
}

select{
casenow=<-after(effective.Sub(now)):
???//entrieswithsametimegetsrun.
for_,entry:=rangec.entries{
ifentry.Next!=effective{
break
}
entry.Prev=now
entry.Next=entry.Schedule.Next(now)
goentry.Job.Run()
}
casee:=<-c.add:
???e.Next?=?e.Schedule.Next(time.Now())
???c.entries?=?append(c.entries,e)
case<-c.stop:
???return//terminatego-routine.
}
}
}

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

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

整體實(shí)現(xià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é)我們就來看看,gron.Every 干了什么事,又是如何支持 At 方法的。

//EveryreturnsaSchedulereoccurseveryperiodp,pmustbeatleast
//time.Second.
funcEvery(ptime.Duration)AtSchedule{

ifp//truncatesuptoseconds

return&periodicSchedule{
period:p,
}
}

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

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

typeperiodicSchedulestruct{
periodtime.Duration
}

//Nextaddstimettounderlyingperiod,truncatesuptounitofseconds.
func(psperiodicSchedule)Next(ttime.Time)time.Time{
returnt.Truncate(time.Second).Add(ps.period)
}

//Atreturnsaschedulewhichreoccurseveryperiodp,attimet(hh:ss).
//
//Note:Atpanicswhenperiodpislessthanxtime.Day,anderrorhh:ssformat.
func(psperiodicSchedule)At(tstring)Schedule{
ifps.periodpanic("periodmustbeatleastindays")
}

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

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

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

//parsenaivelytokeniseshoursandminutes.
//
//returnserrorwheninputformatwasincorrect.
funcparse(hhmmstring)(hhint,mmint,errerror){

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

ifhh0||hh>24{
hh,mm=0,0
err=errors.New("invalidhhformat")
}
ifmm0||mm>59{
hh,mm=0,0
err=errors.New("invalidmmformat")
}

return
}

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

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

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

  • 若周期連 1 天都不到,不支持 At 能力,因?yàn)?At 本質(zhì)是在選定的一天內(nèi),指定小時(shí),分鐘,作為輔助。連一天都不到的周期,是要精準(zhǔn)處理的;

  • 將用戶輸入的形如 "23:59" 時(shí)間字符串解析出來【小時(shí)】和【分鐘】;

  • 構(gòu)建出一個(gè) atSchedule 對象,包含了【周期時(shí)長】,【小時(shí)】,【分鐘】。

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

typeatSchedulestruct{
periodtime.Duration
hhint
mmint
}

//resetreturnsnewDatebasedontimeinstantt,andreconfigureitshh:ss
//accordingtoatSchedule'shh:ss.
func(asatSchedule)reset(ttime.Time)time.Time{
returntime.Date(t.Year(),t.Month(),t.Day(),as.hh,as.mm,0,0,time.UTC)
}

//Nextreturns**next**time.
//iftpasseditssupposedschedule:reset(t),returnsreset(t)+period,
//elsereturnsreset(t).
func(asatSchedule)Next(ttime.Time)time.Time{
next:=as.reset(t)
ift.After(next){
returnnext.Add(as.period)
}
returnnext
}

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

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

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

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

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

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

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

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

//AtScheduleextendsSchedulebyenablingperiodic-interval&time-specificsetup
typeAtScheduleinterface{
At(tstring)Schedule
Schedule
}

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

擴(kuò)展性

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

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

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

typeReminderstruct{
Msgstring
}

func(rReminder)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 即可。

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

packagemain

import(
"fmt"
"github.com/roylee0704/gron"
"github.com/roylee0704/gron/xtime"
)

typePrintJobstruct{Msgstring}

func(pPrintJob)Run(){
fmt.Println(p.Msg)
}

funcmain(){

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)

//contrivedjobs
purgeTask=func(){fmt.Println("purgeagedrecords")}
printFoo=printJob{"Foo"}
printBar=printJob{"Bar"}
)

c:=gron.New()

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

//JobsmayalsobeaddedtoarunningGron
c.Add(monthly,printBar)
c.AddFunc(yearly,purgeTask)

//StopGron(runningjobsarenothalted).
c.Stop()
}

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

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

typeCronstruct{
stopchanstruct{}
}

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

func(c*Cron)run(){

for{
select{
case<-c.stop:
???return//terminatego-routine.
}
}
}

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

結(jié)語

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

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

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

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

審核編輯 :李倩



聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 操作系統(tǒng)
    +關(guān)注

    關(guān)注

    37

    文章

    6825

    瀏覽量

    123331
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4788

    瀏覽量

    68612

原文標(biāo)題:解析 Golang 定時(shí)任務(wù)庫 gron 設(shè)計(jì)和原理

文章出處:【微信號:LinuxHub,微信公眾號:Linux愛好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    Linux計(jì)劃任務(wù)介紹

    點(diǎn)定時(shí)備份數(shù)據(jù)。比如:11點(diǎn)開啟網(wǎng)站搶購接口,12點(diǎn)關(guān)閉網(wǎng)站搶購接口。 3.計(jì)劃任務(wù)主要分為以下兩種使用情況: 1.系統(tǒng)級別的定時(shí)任務(wù): 臨時(shí)文件清理、系統(tǒng)信息采集、日志文件切割?2.用戶級別的
    的頭像 發(fā)表于 11-24 15:49 ?287次閱讀

    定時(shí)器技術(shù):Air780E如何革新定時(shí)任務(wù)管理?

    今天講的是關(guān)于Air780E如何革新定時(shí)任務(wù)管理的內(nèi)容,希望大家有所收獲。
    的頭像 發(fā)表于 11-07 13:50 ?234次閱讀
    <b class='flag-5'>定時(shí)</b>器技術(shù):Air780E如何革新<b class='flag-5'>定時(shí)任務(wù)</b>管理?

    mysql定時(shí)備份任務(wù)

    在生產(chǎn)環(huán)境上,為了避免數(shù)據(jù)的丟失,通常情況下都會定時(shí)的對數(shù)據(jù)進(jìn)行備份。而Linux的crontab指令則可以幫助我們實(shí)現(xiàn)對數(shù)據(jù)定時(shí)進(jìn)行備份。首先我們來簡單了解crontab指令,如
    的頭像 發(fā)表于 10-31 10:07 ?164次閱讀

    陀螺儀LSM6DSOW開發(fā)(5)----MotionFX解析空間坐標(biāo)

    和配置MotionFX,使用FIFO讀取傳感器數(shù)據(jù),F(xiàn)IFO可以作為數(shù)據(jù)緩沖區(qū),存儲傳感器的臨時(shí)數(shù)據(jù)。這樣可以防止數(shù)據(jù)丟失,特別是在處理器忙于其他任務(wù)時(shí),并利用這些數(shù)據(jù)進(jìn)行空間坐標(biāo)的解析。本章案例使用上節(jié)的demo進(jìn)行修改。
    的頭像 發(fā)表于 08-15 18:13 ?1673次閱讀
    陀螺儀LSM6DSOW開發(fā)(5)----MotionFX<b class='flag-5'>庫</b><b class='flag-5'>解析</b>空間坐標(biāo)

    linux定時(shí)任務(wù)的用法總結(jié)

    習(xí)慣了使用 windows 的計(jì)劃任務(wù),使用 linux 中的 crontab 管理定時(shí)任務(wù)時(shí)很不適應(yīng)。
    的頭像 發(fā)表于 08-14 18:16 ?851次閱讀
    linux<b class='flag-5'>定時(shí)任務(wù)</b>的用法總結(jié)

    ESP8266如何實(shí)現(xiàn)時(shí)間小于3us的定時(shí)任務(wù)

    想實(shí)現(xiàn)一個(gè)穩(wěn)定的軟串口,現(xiàn)有的軟串口程序是通過中斷實(shí)現(xiàn)的,但中斷好像會被其他中斷打斷,導(dǎo)致數(shù)據(jù)丟失,定時(shí)器按文檔上的說法,只能大于50us,能不能實(shí)現(xiàn)時(shí)間小于3us的定時(shí)任務(wù)或者提高GPIO中斷的優(yōu)先級呢?或者還有其他什么辦法?
    發(fā)表于 07-19 06:13

    陀螺儀LSM6DSV16X與AI集成(8)----MotionFX解析空間坐標(biāo)

    和配置MotionFX,使用FIFO讀取傳感器數(shù)據(jù),F(xiàn)IFO可以作為數(shù)據(jù)緩沖區(qū),存儲傳感器的臨時(shí)數(shù)據(jù)。這樣可以防止數(shù)據(jù)丟失,特別是在處理器忙于其他任務(wù)時(shí),并利用這些數(shù)據(jù)進(jìn)行空間坐標(biāo)的解析。本章案例使用上節(jié)的demo進(jìn)行修改。
    的頭像 發(fā)表于 07-18 10:43 ?1186次閱讀
    陀螺儀LSM6DSV16X與AI集成(8)----MotionFX<b class='flag-5'>庫</b><b class='flag-5'>解析</b>空間坐標(biāo)

    智能插座“云”時(shí)代:定時(shí)任務(wù)與事件驅(qū)動的創(chuàng)新管理

    用戶可以通過云端界面,在任何時(shí)間任何地點(diǎn)對插座進(jìn)行配置和監(jiān)控,同時(shí)收集數(shù)據(jù)和洞察分析,以促進(jìn)能效最優(yōu)化。無論是確保家中的咖啡機(jī)在你醒來之前準(zhǔn)備好早晨的咖啡,還是遠(yuǎn)程調(diào)整辦公室的溫度設(shè)置以節(jié)約能源,智能插座配合云管理打開了便捷與高效的大門。
    的頭像 發(fā)表于 07-15 18:16 ?975次閱讀
    智能插座“云”時(shí)代:<b class='flag-5'>定時(shí)任務(wù)</b>與事件驅(qū)動的創(chuàng)新管理

    長持續(xù)時(shí)間定時(shí)器電路圖 時(shí)間定時(shí)器的工作原理和功能

    的處理,都離不開定時(shí)器的精確控制。時(shí)間定時(shí)器通常由硬件和軟件兩部分組成,硬件部分通過計(jì)時(shí)器芯片或計(jì)數(shù)器來實(shí)現(xiàn)時(shí)間的度量和計(jì)算,而軟件部分則是通過編程語言提供的函數(shù)或類來設(shè)置和處理定時(shí)任務(wù)
    的頭像 發(fā)表于 06-24 17:34 ?1909次閱讀
    長持續(xù)時(shí)間<b class='flag-5'>定時(shí)</b>器電路圖 時(shí)間<b class='flag-5'>定時(shí)</b>器的工作原理和功能

    在物通博聯(lián)工業(yè)智能網(wǎng)關(guān)的本地配置界面(WEB)直接配置定時(shí)控制任務(wù)

    開關(guān),可實(shí)現(xiàn)全年定時(shí)任務(wù)自動執(zhí)行。在多個(gè)任務(wù)日期重疊時(shí),可選執(zhí)行高等級的還是并行執(zhí)行。設(shè)有一個(gè)遠(yuǎn)程和本地的控制點(diǎn),默認(rèn)為本地狀態(tài),網(wǎng)關(guān)自己執(zhí)行設(shè)置的任務(wù),當(dāng)用戶將改控制點(diǎn)切換為遠(yuǎn)程時(shí)可進(jìn)行遠(yuǎn)程一鍵開關(guān)或者指定
    的頭像 發(fā)表于 04-24 17:21 ?537次閱讀
    在物通博聯(lián)工業(yè)智能網(wǎng)關(guān)的本地配置界面(WEB)直接配置<b class='flag-5'>定時(shí)</b>控制<b class='flag-5'>任務(wù)</b>

    ArkTS語言基礎(chǔ)類-解析

    多線程并發(fā),支持Worker線程和宿主線程之間進(jìn)行通信,開發(fā)者需要主動創(chuàng)建和關(guān)閉Worker線程。 提供常見的[容器類增、刪、改、查]的能力。 提供XML、URL、URI構(gòu)造和解析的能力。 XML
    發(fā)表于 02-20 16:44

    使用TC21x的GPT實(shí)現(xiàn)1m計(jì)時(shí)器執(zhí)行定時(shí)任務(wù),怎么配置GTM和GPT?

    專家們好,我想使用TC21x的GPT實(shí)現(xiàn)1m計(jì)時(shí)器執(zhí)行定時(shí)任務(wù),不知道怎么配置GTM和GPT?
    發(fā)表于 02-06 06:47

    鴻蒙原生應(yīng)用/元服務(wù)開發(fā)-長時(shí)任務(wù)

    概述 功能介紹 應(yīng)用退至后臺后,對于在后臺需要長時(shí)間運(yùn)行用戶可感知的任務(wù),例如播放音樂、導(dǎo)航等。為防止應(yīng)用進(jìn)程被掛起,導(dǎo)致對應(yīng)功能異常,可以申請長時(shí)任務(wù),使應(yīng)用在后臺長時(shí)間運(yùn)行。申請長時(shí)任務(wù)后,系統(tǒng)
    發(fā)表于 01-09 10:52

    如何使用Golang連接MySQL

    首先我們來看如何使用Golang連接MySQL。
    的頭像 發(fā)表于 01-08 09:42 ?3379次閱讀
    如何使用<b class='flag-5'>Golang</b>連接MySQL

    任務(wù)調(diào)度系統(tǒng)設(shè)計(jì)的核心邏輯

    Redis的讀寫性能極好,分布式鎖也比Quartz數(shù)據(jù)行級鎖更輕量級。當(dāng)然Redis鎖也可以替換成Zookeeper鎖,也是同樣的機(jī)制。 在小型項(xiàng)目中,使用:定時(shí)任務(wù)框架(Quartz/Spring Schedule)和 分布式鎖(redis/zookeeper)
    的頭像 發(fā)表于 01-02 15:09 ?888次閱讀
    <b class='flag-5'>任務(wù)</b>調(diào)度系統(tǒng)設(shè)計(jì)的核心邏輯
    主站蜘蛛池模板: 午夜乩伦| 天堂中文字幕| 色玖玖| 国产区一区二区三区| 天天干天天碰| videosxxoo18欧美| aⅴ一区二区三区| 欧美午夜色视频国产精品| 在线色片| 欧美xxx69| 四虎国产在线| 艹逼免费视频| 日本69式xxx视频| 亚洲国产高清精品线久久| 老色批视频| 欧美猛交喷潮在线播放| 老师办公室高h文小说| 欧美成人三级伦在线观看| 天天操夜夜欢| 国产精品久久久久久免费播放 | 亚洲插| 亚洲一区二区中文| 天天综合天天干| 欧美 亚洲 一区| 久久综合99| 国产精品久久久久久久久久影院| 中文字幕一精品亚洲无线一区| 久久久噜噜噜久久中文字幕色伊伊| 男人天堂网2021| 亚洲欧洲色| 91正在 播放| 亚洲涩综合| 九色欧美| 亚洲乱淫| 人人插人人爱| 精品午夜视频| 四虎国产精品免费视| 天天搞天天干| 亚洲最大成人综合网| 97福利影院| 高清一区二区在线观看|