重试包装器
难度:⭐⭐ 中等
考点
- 指数退避(Exponential Backoff)
- 泛型重试函数
- 接口断言判断可重试性
提示
- 循环 maxRetries+1 次(含首次执行)
- 每次失败后 sleep,时间按 multiplier 增长,不超过 maxWait
- ShouldRetry 用类型断言
err.(Retryable)判断
参考答案(Go)
点击展开参考答案
go
//go:build ignore
package answer
import "time"
type RetryConfig struct {
MaxRetries int
InitialWait time.Duration
MaxWait time.Duration
Multiplier float64
}
func DefaultConfig() RetryConfig {
return RetryConfig{MaxRetries: 3, InitialWait: 100 * time.Millisecond, MaxWait: 5 * time.Second, Multiplier: 2.0}
}
func Retry(fn func() error, config RetryConfig) error {
var err error
wait := config.InitialWait
for attempt := 0; attempt <= config.MaxRetries; attempt++ {
err = fn()
if err == nil {
return nil
}
if attempt < config.MaxRetries {
time.Sleep(wait)
wait = time.Duration(float64(wait) * config.Multiplier)
if wait > config.MaxWait {
wait = config.MaxWait
}
}
}
return err
}
func RetryWithResult[T any](fn func() (T, error), config RetryConfig) (T, error) {
var result T
var err error
wait := config.InitialWait
for attempt := 0; attempt <= config.MaxRetries; attempt++ {
result, err = fn()
if err == nil {
return result, nil
}
if attempt < config.MaxRetries {
time.Sleep(wait)
wait = time.Duration(float64(wait) * config.Multiplier)
if wait > config.MaxWait {
wait = config.MaxWait
}
}
}
return result, err
}
type Retryable interface {
IsRetryable() bool
}
func ShouldRetry(err error) bool {
if err == nil {
return false
}
if r, ok := err.(Retryable); ok {
return r.IsRetryable()
}
return true
}