详解指数退避算法 exponential backoff algorithm
指数退避(Exponential backoff)在网络请求中的应用🌟🌟🌟🌟🌟
概念
指数退避算法是通过反馈,成倍的降低某个进程的速率,以逐渐找到合适速率的算法。它是一种闭环控制系统,用来降低受控进程对不良事件的回应速度。在无线电网络和计算机网络中应用广泛。
建模函数为:
t = b c t=b^c t=bc, 由此可见随着请求失败的次数的增加,两次请求之间的间隔也在指数式增加,其中
t: 两个 action 之间的延迟时间
b: 基数, 通常我们使用二进制指数退避算法时,它就是 2
c: 发生不良事件的次数,比如请求失败的次数
注:
- 通常如果不良事件发生频率降低,t 会逐渐的恢复,但为了避免震荡恢复速度肯定要比速率降低速度慢
- t 并不是准确的延迟时间,很多情况下它是延迟时间的上限
应用
处理 rate limit 限流
发生限流后可以使用指数退避策略重新发起请求:
通常:遇到限流报错时,传统的重试策略是每隔一段时间重试一次。但由于是固定的时间重试一次,重试时又会有大量的请求在同一时刻涌入,会不断地造成限流。但使用 exponential backoff 之后能完美避免这种情况:
在 c
次碰撞后(比如请求失败),会选择 0 和
2
c
−
1
2^c-1
2c−1 之间的随机值作为时隙的数量。
- 对于第 1 次碰撞来说,每个发送者将会等待 0 或 1 个时隙进行发送。
- 而在第 2 次碰撞后,发送者将会等待 0 到 3( 由 2 2 − 1 2^2-1 22−1 计算得到)个时隙进行发送。
- 而在第 3 次碰撞后,发送者将会等待 0 到 7( 由 2 3 − 1 2^3-1 23−1 计算得到)个时隙进行发送。
- 以此类推……
随着重传次数的增加,延迟的程度也会指数增长。
重试策略
像建立网络连接、请求公共服务
import (
"time"
"github.com/cenkalti/backoff/v4"
)
type BackoffOption func() backoff.BackOff
// 指定最长重试时间
func MaxSecondsLimit(seconds int) BackoffOption {
return func() backoff.BackOff {
back := backoff.NewExponentialBackOff()
back.MaxElapsedTime = time.Duration(seconds) * time.Second
// 设置间隔是 1s default: 60s
back.InitialInterval = 1 * time.Second
return back
}
}
// 重试方法
func ExponentialBackoffRetry(f backoff.Operation, options ...BackoffOption) error {
// 设置最长等待 60s,default: 15 min
var cfg backoff.BackOff = MaxSecondsLimit(60)()
for _, opt := range options {
if opt != nil {
cfg = opt()
}
}
return backoff.Retry(f, cfg)
}
func NoRetry() BackoffOption {
return func() backoff.BackOff {
back := &backoff.StopBackOff{}
return back
}
}
// 使用方式一:使用指数退避重试
err := ExponentialBackoffRetry(func() error{
resp, err := request('xx')
return err
})
// 使用方式二:不使用重试
err := ExponentialBackoffRetry(func() error{
resp, err := request('xx')
return err
}, NoRetry())
python 版本
import backoff
class MyException(Exception):
def __init__(self, message, status):
super().__init__(message, status)
self.message = message
idx = 0
# 最长重试时间是 20s
@backoff.on_exception(
backoff.expo, MyException, max_time=20)
def test_backoff(**params):
print("=====>", idx)
idx += 1
raise MyException("test")
其他
像 celery(分布式异步消息任务队列) 中 celery/utils/time.py 使用指数退避算法避免多个任务同时执行