2019年6月25日火曜日

golang でリトライ処理を試してみる

概要

retry-go というライブラリがあったので試してみました

環境

  • macOS 10.14.5
  • go 1.11.5
  • dep 0.5.0
    • retry-go 2.3.0

基本

  • vim $GOPATH/src/github.com/hawksnowlog/a/main.go
package main

import (
    "errors"
    "fmt"
    "github.com/avast/retry-go"
    "math/rand"
    "time"
)

func random() error {
    rand.Seed(time.Now().UnixNano())
    n := rand.Intn(10)
    if n >= 5 {
        return nil
    }
    return errors.New("error")
}

func main() {
    fmt.Println("start")
    err := retry.Do(
        func() error {
            ret := random()
            return ret
        },
        retry.DelayType(func(n uint, config *retry.Config) time.Duration {
            fmt.Println(n)
            return 0
        }),
    )
    fmt.Println(err)
}
  • dep init
  • go fmt github.com/hawksnowlog/a
  • go build github.com/hawksnowlog/a
  • ./a

少し解説

random メソッドは今回のロジックには関係ないので省略します
内容は 0 から 9 の数字をランダムに返すだけです

リトライさせたい処理を retry.Do で囲みます
デフォルトではリトライ回数は 10 になっています
後でリトライ回数を変更する方法を紹介します
retry.Do メソッドは 2 つの引数を持ちます
一つ目はリトライさせたい関数でもう一つは Option になります
リトライする条件は対象のメソッドが error の場合にリトライします
errornil の場合にはリトライせず次の処理に進みます

上記では Option の部分に retry.DelayType メソッドを指定しています
このメソッドの返り値が Option になっています
さらに retry.DelayType にはメソッドを指定できます
これはもし error が返ってきてリトライする場合にリトライ前に実施したい処理を指定することができます
上記ではリトライ前に fmt.Println しかしていませんが何かさせたい場合にはここに記載します
n uint にはリトライ回数が代入されています
もしデフォルトの 10 回のリトライの場合には 0 からカウントして最大 8 までインクリメントします

返り値が time.Duration になっていますがここを 0 以外にするとリトライ前にウェイトさせることができます

リトライ後に少しディレイを持たせる

例えばリトライ回数 x 1秒待たせる場合には以下のようになります

func main() {
    fmt.Println("start")
    err := retry.Do(
        func() error {
            ret := random()
            return ret
        },
        retry.DelayType(func(n uint, config *retry.Config) time.Duration {
            fmt.Println(n)
            return time.Duration(n) * time.Second
        }),
    )
    fmt.Println(err)
}

return time.Duration(n) * time.Second とすることで実現できます

リトライ回数を変更する

retry.Attempts を使います
これも Option を返すので retry.DelayType の後に続いて書けば OK です

func main() {
    fmt.Println("start")
    err := retry.Do(
        func() error {
            ret := random()
            return ret
        },
        retry.DelayType(func(n uint, config *retry.Config) time.Duration {
            fmt.Println(n)
            return time.Duration(n) * time.Second
        }),
        retry.Attempts(3),
    )
    fmt.Println(err)
}

上記の場合は 10 -> 3 回リトライします

最後に

retry-go を試してみました
既存の処理に対してリトライ処理を入れるのも簡単にできます

またリトライ時には必ずといっていいほど出る Exponential Backoff も簡単に実装できます (参考: BackOffDelay)

参考サイト

0 件のコメント:

コメントを投稿