-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathretry.go
More file actions
101 lines (89 loc) · 2.17 KB
/
retry.go
File metadata and controls
101 lines (89 loc) · 2.17 KB
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
package samhook
import (
"math"
"math/rand"
"time"
)
// RetryOptions 重試選項
type RetryOptions struct {
MaxRetries int
Interval time.Duration
Backoff *ExponentialBackoff
}
// ExponentialBackoff 指數退避
type ExponentialBackoff struct {
InitialInterval time.Duration
MaxInterval time.Duration
Multiplier float64
Jitter bool
}
// DefaultRetryOptions 預設重試選項
var DefaultRetryOptions = RetryOptions{
MaxRetries: 3,
Interval: 1 * time.Second,
Backoff: &ExponentialBackoff{
InitialInterval: 1 * time.Second,
MaxInterval: 30 * time.Second,
Multiplier: 2.0,
Jitter: true,
},
}
// NextInterval 計算下一次重試間隔
func (eb *ExponentialBackoff) NextInterval(attempt int) time.Duration {
interval := float64(eb.InitialInterval) * math.Pow(eb.Multiplier, float64(attempt))
if interval > float64(eb.MaxInterval) {
interval = float64(eb.MaxInterval)
}
if eb.Jitter {
// 添加隨機抖動(±10%)
jitter := interval * 0.1 * (rand.Float64()*2 - 1)
interval += jitter
}
return time.Duration(interval)
}
// SendWithRetry 帶重試的發送,支援自訂客戶端配置
func SendWithRetry(url string, msg Message, opts RetryOptions, clientOpts ...ClientOption) error {
var lastErr error
interval := opts.Interval
for i := 0; i <= opts.MaxRetries; i++ {
err := SendWithOptions(url, msg, clientOpts...)
if err == nil {
return nil
}
// 檢查是否可重試
if webhookErr, ok := err.(*WebhookError); ok {
if !isRetryable(webhookErr) {
return err
}
} else {
// 非 WebhookError 預設不重試
return err
}
lastErr = err
if i < opts.MaxRetries {
// 計算重試間隔
if opts.Backoff != nil {
interval = opts.Backoff.NextInterval(i)
}
time.Sleep(interval)
}
}
return lastErr
}
// isRetryable 判斷錯誤是否可重試
func isRetryable(err *WebhookError) bool {
// 網路錯誤可以重試
if err.IsNetworkError() {
return true
}
// 5xx 錯誤可以重試
if err.IsAPIError() && err.StatusCode >= 500 {
return true
}
// 429 速率限制可以重試
if err.IsAPIError() && err.StatusCode == 429 {
return true
}
// 4xx 錯誤不重試
return false
}