1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
| package retry
import ( "context" "log" "math/rand" "time" )
func Retry(ctx context.Context, call func() (stop bool, err error), opts ...func(*Backoff)) (lastErr error) { rand.Seed(time.Now().UnixNano()) bo := NewBackOff(opts...) for { stop, err := call() if err == nil { return nil } lastErr = err if stop { return lastErr } bo.count++ if bo.count == bo.Times { return lastErr } p := bo.Pause()
log.Printf("retry %d, %0.2fs, %v\n", bo.count, p.Seconds(), err)
t := time.NewTimer(p) select { case <-ctx.Done(): t.Stop() return ctx.Err() case <-t.C: } } }
type Backoff struct { Initial time.Duration
Max time.Duration
Times int
Multiplier float64
cur time.Duration
count int }
func (bo *Backoff) Pause() time.Duration { d := time.Duration(1 + rand.Int63n(int64(bo.cur))) bo.cur = time.Duration(float64(bo.cur) * bo.Multiplier) if bo.cur > bo.Max { bo.cur = bo.Max } return d }
func NewBackOff(opts ...func(*Backoff)) *Backoff { bo := &Backoff{ Initial: time.Second, cur: time.Second, Max: 30 * time.Second, Times: 4, Multiplier: 2, }
for _, optFunc := range opts { optFunc(bo) }
return bo }
func WithInitial(t time.Duration) func(*Backoff) { return func(bo *Backoff) { bo.Initial = t } }
func WithMax(t time.Duration) func(*Backoff) { return func(bo *Backoff) { bo.Max = t } }
func WithMultiplier(t float64) func(*Backoff) { return func(bo *Backoff) { bo.Multiplier = t } }
func WithRetryTimes(t int) func(backoff *Backoff) { return func(bo *Backoff) { bo.Times = t } }
|