github.com/qiniu/go-sdk/v7@v7.27.0/storagev2/backoff/backoff.go (about)

     1  package backoff
     2  
     3  import (
     4  	"context"
     5  	"math"
     6  	"math/rand"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/alex-ant/gomath/rational"
    11  )
    12  
    13  type (
    14  	// Backoff 退避器接口
    15  	Backoff interface {
    16  		// Time 获取重试的退避时长间隔
    17  		Time(context.Context, *BackoffOptions) time.Duration
    18  	}
    19  
    20  	// BackoffOptions 退避器选项
    21  	BackoffOptions struct {
    22  		// Attempts 重试次数
    23  		Attempts int
    24  	}
    25  )
    26  
    27  type customizedBackoff struct {
    28  	backoffFn func(context.Context, *BackoffOptions) time.Duration
    29  }
    30  
    31  // NewBackoff 创建自定义时长的退避器
    32  func NewBackoff(fn func(context.Context, *BackoffOptions) time.Duration) Backoff {
    33  	return customizedBackoff{backoffFn: fn}
    34  }
    35  
    36  func (s customizedBackoff) Time(ctx context.Context, options *BackoffOptions) time.Duration {
    37  	return s.backoffFn(ctx, options)
    38  }
    39  
    40  type fixedBackoff struct {
    41  	wait time.Duration
    42  }
    43  
    44  // NewFixedBackoff 创建固定时长的退避器
    45  func NewFixedBackoff(wait time.Duration) Backoff {
    46  	return fixedBackoff{wait: wait}
    47  }
    48  
    49  func (s fixedBackoff) Time(context.Context, *BackoffOptions) time.Duration {
    50  	return s.wait
    51  }
    52  
    53  type randomizedBackoff struct {
    54  	base                        Backoff
    55  	minification, magnification rational.Rational
    56  	r                           *rand.Rand
    57  	mutex                       sync.Mutex
    58  }
    59  
    60  // NewRandomizedBackoff 创建随机时长的退避器
    61  func NewRandomizedBackoff(base Backoff, minification, magnification rational.Rational) Backoff {
    62  	if minification.LessThanNum(0) {
    63  		panic("minification must be greater than or equal to 0")
    64  	}
    65  	if magnification.LessThanNum(0) || magnification.GetNumerator() == 0 {
    66  		panic("magnification must be greater than 0")
    67  	}
    68  	return &randomizedBackoff{
    69  		base:          base,
    70  		minification:  minification,
    71  		magnification: magnification,
    72  		r:             rand.New(rand.NewSource(time.Now().UnixNano())),
    73  	}
    74  }
    75  
    76  func (s *randomizedBackoff) Time(ctx context.Context, opts *BackoffOptions) time.Duration {
    77  	b := s.base.Time(ctx, opts)
    78  	min := s.minification.MultiplyByNum(int64(b))
    79  	max := s.magnification.MultiplyByNum(int64(b))
    80  	diff := int64(max.Subtract(min).Float64())
    81  	s.mutex.Lock()
    82  	r := s.r.Int63n(diff)
    83  	s.mutex.Unlock()
    84  	return time.Duration(min.AddNum(r).Float64())
    85  }
    86  
    87  type limitedBackoff struct {
    88  	base     Backoff
    89  	min, max time.Duration
    90  }
    91  
    92  // NewLimitedBackoff 创建限制时长的退避器
    93  func NewLimitedBackoff(base Backoff, min, max time.Duration) Backoff {
    94  	return &limitedBackoff{
    95  		base: base,
    96  		min:  min,
    97  		max:  max,
    98  	}
    99  }
   100  
   101  func (s limitedBackoff) Time(ctx context.Context, opts *BackoffOptions) time.Duration {
   102  	b := s.base.Time(ctx, opts)
   103  	if b < s.min {
   104  		return s.min
   105  	} else if b > s.max {
   106  		return s.max
   107  	}
   108  	return b
   109  }
   110  
   111  type exponentialBackoff struct {
   112  	wait       time.Duration
   113  	baseNumber int64
   114  }
   115  
   116  // NewExponentialBackoff 创建时长指数级增长的退避器
   117  func NewExponentialBackoff(wait time.Duration, baseNumber int64) Backoff {
   118  	return exponentialBackoff{wait: wait, baseNumber: baseNumber}
   119  }
   120  
   121  func (e exponentialBackoff) Time(ctx context.Context, opts *BackoffOptions) time.Duration {
   122  	return e.wait * time.Duration(math.Pow(float64(e.baseNumber), float64(opts.Attempts)))
   123  }