详解指数退避算法

详解指数退避算法 exponential backoff algorithm

Exponential backoff🌟🌟🌟

指数退避(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 2c1 之间的随机值作为时隙的数量。

  • 对于第 1 次碰撞来说,每个发送者将会等待 0 或 1 个时隙进行发送。
  • 而在第 2 次碰撞后,发送者将会等待 0 到 3( 由 2 2 − 1 2^2-1 221 计算得到)个时隙进行发送。
  • 而在第 3 次碰撞后,发送者将会等待 0 到 7( 由 2 3 − 1 2^3-1 231 计算得到)个时隙进行发送。
  • 以此类推……

随着重传次数的增加,延迟的程度也会指数增长。

重试策略

像建立网络连接、请求公共服务

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 使用指数退避算法避免多个任务同时执行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值