Go 封装一个重试函数

一个挺好用的重试函数封装

https://www.awsarchitectureblog.com/2015/03/backoff.html 采用这个链接里的重试方案,sleep 时长按指数增大至最大值,每一次取 sleep 时长时,会随机取 [1, currTime] 区间中的一个值。

实现的代码取自 https://github.com/square/quotaservice/blob/master/vendor/cloud.google.com/go/internal/retry.gohttps://github.com/googleapis/gax-go/blob/master/v2/call_option.go

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 第一次的 retry envelope
Initial time.Duration

// Max 最大的 retry envelope
Max time.Duration

// Times 重试次数
Times int

// Multiplier retry envelope 每次增加乘以的数,必须大于 1
Multiplier float64

// cur 当前的 retry envelope
cur time.Duration

// count 当前重试次数
count int
}

func (bo *Backoff) Pause() time.Duration {
// Select a duration between 1ns and the current max. It might seem
// counterintuitive to have so much jitter, but
// https://www.awsarchitectureblog.com/2015/03/backoff.html argues that
// that is the best strategy.
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
}
}