13
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Go4Advent Calendar 2019

Day 1

Goでつくるdaemon処理レシピ集

Last updated at Posted at 2019-11-30

本記事は、アドベントカレンダー Go4 の1日目の記事です。

これは何?

みなさんは、どんなdaemon処理を書いていますか?

ここでは、Go の daemon 処理サンプルを紹介します.
daemon とは、バックグラウンドで動作するプロセス実行し続けるアレで、typoに注意なやつです(いつもdeamonと書いてしまう).

Goではその言語特性から、様々な処理をシンプルに記述しやすいと感じており、daemonの実装例を通してGoらしさを学ぶことにも役立つと思います.
他にも役立つレシピがあれば、教えて下さい :smiley:

まずは基本から

単純に無限実行させる

The Go Playground で実行してみる

func main() {
	timeout := 5 * time.Second
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
	f := func() error { // some task
		return nil
	}

  simple(ctx, f)
}

func simple(ctx context.Context, task func() error) {
	for {
		err := task()
		if err != nil {
			log.Printf("[ERROR] err: %v", err)
		}
		time.Sleep(500 * time.Millisecond)
	}
}

context を使ったタイムアウト

The Go Playgroundで実行してみる

func withTimeout(ctx context.Context, task func() error) {
	child, childCancel := context.WithCancel(ctx)
	defer childCancel()

	for {
		err := task()
		if err != nil {
			log.Printf("[ERROR] err: %v", err)
		}
		select {
		case <-child.Done():
			log.Printf("[DEBUG] timeout")
			return
		default:
			time.Sleep(500 * time.Millisecond)
		}
	}
}

応用編

特定時刻にだけ処理をする

The Go Playground で実行してみる


func runDaemon(ctx context.Context, f func(context.Context) error) {
	daemonHour := "7-10"
	waitDuration := 500 * time.Millisecond

	for {
		now := time.Now()
		if len(daemonHour) != 0 { //起動時刻の指定があったら
			isExec := isExecHour(now, daemonHour)
			log.Printf("[DEBUG] daemon起動時間かどうかの判定 now:%v, daemonHour:%s, isExec:%v", now, daemonHour, isExec)
			if !isExec {
				time.Sleep(1 * time.Minute)
				continue
			}
		}

		err := f(ctx)
		if err != nil {
			log.Printf("[ERRROR] err:%v", err)
		}
		time.Sleep(waitDuration)
	}
}

func isExecHour(now time.Time, dHour string) bool {
	delimitor := "-"
	dh := strings.Split(dHour, delimitor)
	if len(dh) <= 1 {
		return false
	}

	start, err := strconv.Atoi(dh[0])
	if err != nil {
    return false
	}
	end, err := strconv.Atoi(dh[1])
	if err != nil {
    return false
	}

	h := now.Hour()
	if start <= h && h <= end {
		return true
	}
	return false
}

一定周期ごとに(前の処理が終わっていなくても)処理を実行する

The Go Playground で実行してみる

func timeTicker(ctx context.Context, task func(context.Context) error) {
	counter := 0
	waitTime := 1 * time.Second
	ticker := time.NewTicker(waitTime)
	defer ticker.Stop()
	child, childCancel := context.WithCancel(ctx)
	defer childCancel()

	for { // deamon化するため無限実行
		select {
		case t := <-ticker.C:
			counter++
			requestID := counter
			log.Println("[DEBUG] START taskNo=", requestID, "t=", t)

			errCh := make(chan error, 1)
			go func() { // 登録したタスクをブロックせずに実行
				errCh <- task(ctx)
			}()

			go func() {
				// error channelにリクエストの結果が返ってくるのを待つ
				select {
				case err := <-errCh:
					if err != nil {
						// Deamonの強制終了
						log.Println("[ERROR] ", err)

					}
					log.Println("[DEBUG] END requestNo=", requestID)
				}
			}()
		case <-child.Done():
			return
		}
	}
}

みなさんも、よき daemon Life をお過ごしください!

13
11
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?