Help us understand the problem. What is going on with this article?

goのchannelを使って失敗してもリトライする処理を書いてみた

More than 1 year has passed since last update.

ある処理を行うデーモンを何個か作って並列に実行し
あるデーモンでエラーが起きたら再起動させる処理を書いてみました。

traceしやすいので
Daemon interface に自身のIDを出せるように設定しました。

interfaceとgoのchannelは便利だなぁと実感できるかと思います。

playground

package main

import (
    "context"
    "errors"
    "fmt"
    "sync"
    "time"
)

type Daemon interface {
    Do(ctx context.Context) chan error
    ID() string
}

type Sample struct {
    interval int
    id       string
}

func (a *Sample) Do(ctx context.Context) chan error {
    errCh := make(chan error)
    go func() {
        time.Sleep(time.Duration(a.interval) * time.Second)
        fmt.Printf("%#v, %#v\n", "fire", a.ID())
        errCh <- errors.New("err!!!")
    }()
    return errCh
}

func (a *Sample) ID() string {
    return a.id
}

type ConnTable struct {
    list map[chan error]Daemon
}

func (a *ConnTable) Start(ctx context.Context, d []Daemon) chan error {
    errCh := make(chan error)
    for _, item := range d {
        ch := item.Do(ctx)
        a.list[ch] = item
    }

    go func() {
        for ch, daemon := range a.list {
            go func(noticeCh chan error, d Daemon) {
                for {
                    err := <-noticeCh
                    fmt.Printf("[%#v] receive Err %#v\n", d.ID(), err)
                    noticeCh = d.Do(ctx)
                }
            }(ch, daemon)

        }
    }()

    return errCh
}

func main() {
    ss := []Daemon{
        &Sample{
            interval: 1,
            id:       "1",
        },
        &Sample{
            interval: 2,
            id:       "2",
        },
    }
    ctx, cancel := context.WithCancel(context.Background())

    ct := &ConnTable{
        list: map[chan error]Daemon{},
    }

    ct.Start(ctx, ss)

    time.Sleep(10 * time.Second)
    cancel()
}

結果

$ go run connpool/main.go
"fire", "1"
["1"] receive Err &errors.errorString{s:"err!!!"}
"fire", "2"
"fire", "1"
["1"] receive Err &errors.errorString{s:"err!!!"}
["2"] receive Err &errors.errorString{s:"err!!!"}
"fire", "1"
["1"] receive Err &errors.errorString{s:"err!!!"}
"fire", "2"
["2"] receive Err &errors.errorString{s:"err!!!"}
"fire", "1"
["1"] receive Err &errors.errorString{s:"err!!!"}
"fire", "1"
["1"] receive Err &errors.errorString{s:"err!!!"}
"fire", "2"
["2"] receive Err &errors.errorString{s:"err!!!"}
"fire", "1"
["1"] receive Err &errors.errorString{s:"err!!!"}
"fire", "1"
["1"] receive Err &errors.errorString{s:"err!!!"}
"fire", "2"
["2"] receive Err &errors.errorString{s:"err!!!"}
"fire", "1"
["1"] receive Err &errors.errorString{s:"err!!!"}
"fire", "1"
["1"] receive Err &errors.errorString{s:"err!!!"}

Todo
再起動時の処理をサーキットブレーカにすればもっとよくなるはず

smith-30
engineer elmをやりたいと思っている
elm-jp
主に日本で活動する Elm 利用者のコミュニティです。
https://elm-lang.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away