Golang中retry.Do的额外功能实现
4 回复
我猜这个就是那个包。
在阅读源代码后(假设我们讨论的是同一个包),我认为你可以实现自己的 DelayTypeFunc,并将其委托给其他延迟函数:
type ChangeableDelayType struct {
Func retry.DelayTypeFunc
}
func (c *ChangeableDelayType) DelayType(n uint, err error, config *Config) time.Duration {
return c.Func(n, err, config)
}
// ...
c := &ChangeableDelayType{
Func: retry.BackOfDelay,
}
retry.Do(
/* ... */,
retry.DelayType(c.DelayType),
)
// 现在你可以通过修改 c.Func 来改变行为,但要注意
// 对 c.Func 的并发访问:可能需要添加互斥锁来
// 同步访问。
对于需要更细粒度控制的场景,你可以考虑使用 github.com/avast/retry-go 包的 Retry 函数配合自定义配置。以下是实现你所需功能的示例:
package main
import (
"errors"
"fmt"
"time"
"github.com/avast/retry-go"
)
func main() {
var allErrors []error
// 自定义错误收集器
errorCollector := func(err error) {
allErrors = append(allErrors, err)
}
// 动态配置
config := &retryConfig{
maxRetries: 3,
maxDelay: 2 * time.Second,
}
err := retry.Do(
func() error {
// 你的业务逻辑
return errors.New("模拟失败")
},
retry.Attempts(uint(config.maxRetries)),
retry.MaxDelay(config.maxDelay),
retry.OnRetry(func(n uint, err error) {
errorCollector(err)
// 动态调整参数
if n == 1 {
config.maxRetries = 5
}
}),
)
// 收集最终错误
if err != nil {
errorCollector(err)
}
fmt.Printf("所有错误: %v\n", allErrors)
fmt.Printf("最终错误: %v\n", err)
}
// 自定义配置结构
type retryConfig struct {
maxRetries int
maxDelay time.Duration
}
对于需要实时修改参数的需求,可以结合上下文(context)实现:
package main
import (
"context"
"errors"
"fmt"
"time"
"github.com/avast/retry-go"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
dynamicConfig := &DynamicRetryConfig{
MaxRetries: 3,
MaxDelay: 1 * time.Second,
}
// 监控goroutine动态更新配置
go monitorAndUpdateConfig(ctx, dynamicConfig)
var errors []error
err := retry.Do(
func() error {
return operation()
},
retry.Attempts(uint(dynamicConfig.MaxRetries)),
retry.MaxDelay(dynamicConfig.MaxDelay),
retry.OnRetry(func(n uint, err error) {
errors = append(errors, err)
fmt.Printf("重试 %d, 当前配置: %+v\n", n, dynamicConfig)
}),
retry.Context(ctx),
)
if err != nil {
errors = append(errors, err)
}
fmt.Printf("收集到的错误: %v\n", errors)
}
func operation() error {
return errors.New("操作失败")
}
type DynamicRetryConfig struct {
MaxRetries int
MaxDelay time.Duration
}
func monitorAndUpdateConfig(ctx context.Context, config *DynamicRetryConfig) {
ticker := time.NewTicker(500 * time.Millisecond)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
// 根据条件动态更新配置
config.MaxDelay = 2 * time.Second
}
}
}
如果需要更高级的控制,可以考虑 github.com/cenkalti/backoff 包:
package main
import (
"errors"
"fmt"
"time"
"github.com/cenkalti/backoff"
)
func main() {
var allErrors []error
b := backoff.NewExponentialBackOff()
b.MaxElapsedTime = 30 * time.Second
operation := func() error {
err := someOperation()
if err != nil {
allErrors = append(allErrors, err)
}
return err
}
err := backoff.Retry(operation, b)
if err != nil {
allErrors = append(allErrors, err)
}
fmt.Printf("所有错误记录: %v\n", allErrors)
}
func someOperation() error {
return errors.New("操作失败")
}
这些方案提供了错误收集和动态配置调整的能力。retry-go 的 OnRetry 回调可以收集每次重试的错误,而通过上下文或外部监控可以实现在重试过程中动态调整参数。

