Go Cron - robfig/cron/v3
https://github.com/robfig/cron
GitHub - robfig/cron: a cron library for go
a cron library for go. Contribute to robfig/cron development by creating an account on GitHub.
github.com
Cron 오픈소스 라이브러리를 사용하여 특정 시간마다 동작하는 기능을 구현한다.
Download
go get github.com/robfig/cron/v3@v3.0.0
Import
import "github.com/robfig/cron/v3"
사용법
사용법은 간단하다. 내가 사용하고자 하는 시간 스케줄링과 기능을 설정해주고 Start 해주면 된다.
func main() {
c := cron.New()
c.AddFunc("* * * * *", func() {
log.Println("Hello, World!")
})
c.Start()
time.Sleep(5*time.Minute)
c.Stop()
}
cron.New()로 생성되는 기본 parser는 Cron wikipedia 페이지에 설명된 표준을 준수한다. 따라서 실행 시간은 기본 Cron Spec 같다. 위에서 설정한 기능은 매 분마다 Hello, World!를 찍는다.
# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
# │ │ ┌───────────── day of the month (1 - 31)
# │ │ │ ┌───────────── month (1 - 12)
# │ │ │ │ ┌───────────── day of the week (0 - 6) (Sunday to Saturday;
# │ │ │ │ │ 7 is also Sunday on some systems)
# │ │ │ │ │
# │ │ │ │ │
# * * * * * <command to execute>
몇 초마다 실행시키고 싶다거나, 혹은 내가 원하는 시간 스펙대로 설정을 변경하고 싶은 경우에 New()에 옵션을 주어 설정을 변경할 수도 있다.
// Seconds field, required
cron.New(cron.WithSeconds())
// Seconds field, optional
cron.New(cron.WithParser(cron.NewParser(
cron.SecondOptional | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor,
)))
cron expression 외에도 미리 정의된 여러 예약어도 사용할 수 있다.
func main() {
c := cron.New()
c.AddFunc("* * * * *", func() {
log.Println("Hello, World!")
})
// Run once a day at midnight
c.AddFunc("@daily", func() {
log.Println("Hello, World!")
})
// Run once an hour at the beginning of the hour
c.AddFunc("@hourly", func() {
log.Println("Hello, World!")
})
// activates after 1 hour, 30 minutes, 10 seconds,
// and then every interval after that.
c.AddFunc("@every 1h30m10s", func() {
log.Println("Hello, World!")
})
c.Start()
/*
Wait....
*/
c.Stop()
}
만약 매 시간마다 동작하는 기능에서 panic이 발생하면 어떻게 될까? 설정해놓은 모든 cronjob이 종료되고 서비스도 종료되는 것일까?
이런 문제를 방지하기 위해 panic 발생 시 Recover 기능을 제공한다.
cron.New(cron.WithChain(
cron.Recover(logger), // or use cron.DefaultLogger
))
cron에서 제공하는 기본 logger가 아니라, 내가 정의한 별도의 logger를 사용하고 싶다면 역시 logger 설정을 통해 변경도 가능하다.
func main() {
logPath := "/Users/cron/log"
if err := os.MkdirAll(logPath, 0666); err != nil {
log.Fatalln("failed to make log", err)
}
date := time.Now().Format("2006-01-02")
fileName := fmt.Sprintf("%v.%v.log", "crontest", date)
logFilePath := path.Join(logPath, fileName)
fpLog, _ := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
cron.New(cron.WithLogger(cron.VerbosePrintfLogger(log.New(fpLog, "", log.LstdFlags|log.Lshortfile))))
// ...
}
테스트 코드
custom logger를 설정하여 매 분마다 발생하는 panic을 recovery해 로그로 남기는 테스트 코드.
package test
import (
"fmt"
"github.com/robfig/cron/v3"
"log"
"os"
"os/signal"
"path"
"syscall"
"testing"
"time"
)
func TestCron(t *testing.T) {
logPath := "/Users/cron/log"
if err := os.MkdirAll(logPath, 0666); err != nil {
log.Fatalln("failed to make log", err)
}
date := time.Now().Format("2006-01-02")
fileName := fmt.Sprintf("%v.%v.log", "crontest", date)
logFilePath := path.Join(logPath, fileName)
fpLog, _ := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
done := make(chan bool, 1)
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
go func() {
for {
select {
case s := <-sig:
log.Println("Get Signal :", s)
done <- true
}
}
}()
c := cron.New(cron.WithChain(cron.Recover(cron.VerbosePrintfLogger(log.New(fpLog, "", log.LstdFlags|log.Lshortfile)))))
c.AddFunc("* * * * *", func() {
panic("panic")
})
log.Println("Start Cron.")
c.Start()
for {
select {
case <-done:
c.Stop()
log.Println("Cron Done.")
return
}
}
}