你真的会重试吗?——重试机制之二进制指数退避机制

Posted 魏小言

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了你真的会重试吗?——重试机制之二进制指数退避机制相关的知识,希望对你有一定的参考价值。

Table of Contents

重试机制

本文介绍系统设计中,常见的重试机制。重点介绍二进制指数规避策略,从原理至实操,希望能为您在日后架构设计中带来些帮助!

重试的必要性

在互联网系统中,不管是基础组件还是业务开发,都依赖这机器、网络、电信号、等硬件、软件,任何流程不能保证100%到位。

在实际场景中,会有各种不同重试的场景,尤其是在分布式架构、微服务之中,业务消费模块、服务依赖等等场景。故重试机制不可或缺的遍布于系统架构设计中。

重试前提

首先明确,重试是对操作失败之后的重新操作以达到操作成功的诉求

那么诉求的前提就是,操作逻辑不能是导致操作失败的原因,不然无论是重试多少次,依旧是失败,全无意义。

一般是由于网络、负载、流控、维护、资源不足等非主观因素导致操作暂时/短时间内失败,故才重试。

重试策略

那么问题来了,既然重试,什么时候重试呢/?

1、瞬时重试;2、等间隔重试;3、随机间隔重试;4、队列重试…….

有同学看到这么多种的重试策略,是不是头大,本来简单的问题,不需要那么多,直接重试就好了;

不错,在某些流量较低、非核心业务、即时要求高、或者特殊场景,直接重试没问题,不会有太大的问题,顶多失败了在重试,重试个3-4次不行在转为异步重试,或者直接放弃。

在一些复杂的场景,分布式、高并发下,需要考虑上述的一些重试机制,因为高并发下,大量请求失败重试会导致服务负载增大、不仅影响服务稳定性,还会导致重试的部分成功率下降,甚至会引起DDos,得不偿失。

故在大型系统架构中,需要慎重考虑重试机制

重试策略分析

第一种,服务失败后立马重试;这种在一些业务RPC调用中「内网」可以看到,配置有重试次数、读写耗时等参数;

存在的问题是,如果操作失败的原因是网络抖动「内网环境稳定较高」,在短时间内重试都会失败……而且会增加服务负载

第二种,为了避免服务负载突增,失败之后会sleep等间隔之后在次重试,这种策略在一定程度上会解决第一种的隐患,但当流量很大的时候,存在大量服务等间隔同时重试的概率,治标不治本。

第三种,由于是随机值,在概率上大大降低了一、二中的问题产生。采用一些退避随机算法,甚至几乎可以完全避免。

第四种,异步+同步策略,安全性极高,队列自身就有削峰增谷的作用,但适用场景有限,依赖队列等其他组件。

二进制指数退避策略

说到这里还没完,干货来了,简单介绍下第三种中的二进制指数退避策略;

网络「共享总线-共享带宽|随机系统」中,想什么时候发送数据就能什么时候发送,只要在发现冲突后遵循指数退避等待相应的时间后再重发即可!

TCP,具有超时重试的策略,网络架构中,地位可以说是无可替代。它的重试时间是由RRT等一系列的参数根据算法计算而来,一般是6s、12s、25s、….75s.

二进制指数退避策略实操过程

二进制指数退避策略等同于TCP内部重试算法「系数更复杂」。

大体就是在重试时,会产生一个【0,2的N次方-1】集合,在集合中选中一个随机值,作为重试时间或重试系数。

N=0
[0]
N=1
[0、1]
N=2
[0、1、2、3]
N=3
[0、1、2、3、4、5、6、7]
...

二进制指数退避策略原理

上面就是具体的策略过程。下面聊下原理:

二进制指数退避,又叫做二分探测法

现在的分布式集群,又叫做共享资源随机系统。服务资源是共享的,但服务流量是随机的。其中的节点彼此互不干涉,不知道系统的全局情况,无状态的分布式系统。

起初节点A直接使用资源,忽略其它节点存在;直到冲突发生,节点A会认为系统中至少存在另外一个节点B,所以节点A把全部使用的资源让给B一半,这样就有两份资源,可以避免冲突了;不,虽然有两份,但无标识谁是谁的随机占用,所以冲突的概率为50%;

像这样,如果出现第三个节点,由于系统所有节点都是公平对等的,这第三个节点对于既有的两个节点的影响是均等的。 也就是说,既有的每一个节点均需要假设有一个新的节点与之竞争。 这个新的节点对既有的两个节点的影响,在概率上是相等的。按照协作系统公平性假设,检测到冲突的节点就需要出让一半的资源给潜在的竞争者。

采取相同的策略,继续二分退避,此时冲突的概率就降低到了25 %;

最终,两个节点之间不冲突的概率随着冲突次数而降低;


如果某个节点第一次遭遇冲突,自身可利用资源承诺自减到1/2;

如果再检测到冲突,自身可利用资源承诺自减1/4;

如果再检测到冲突,自身可利用资源承诺自减1/8;


把这个基本事实归纳下去。所以,结论就是:
不管系统中当前有多少节点,只要检测到冲突/超时,检测节点均要假设有一个节点与之进行公平对等竞争,检测节点均需要分配一半资源给与之冲突的节点:
假设k为冲突次数,那么节点需要退避到的资源使用率就是(1/2)K ,由于网络分层协议的保证,为了保证效率,k一般会有最大值约束,超过该最值,则传输放弃。

以上这些,就是二进制指数退避算法的具体操作原理。

Q&A

1、有同学要问了,读了退避策略原理,为什么感觉和过程、重试场景关系不大/?

重试的实质是请求成功,分布式场景高并发下,合理规避冲突,就是提高了请求成功率,同时避免了服务高负载,细品;

2、退避策略,见过其他的,有随机策略、等间隔策略,这个二进制指数退避有什么区别,或者可取之处/?

退避策略,广义上,针对重试做的任何策略,都是为了退避,也就是都是退避策略;随机策略、等间隔策略就是

文中的一、二…种策略;二进制规避策略,更具有针对性,从理论上来讲,效果更好,比某些需要依据经验数据拍板的更科学。

3、一些文章说TCP的超时重试机制用的也是二进制指数退避,为什么间隔体现不同呢/?

TCP超时重试机制,实质算法要相对复杂,依赖网络中的数据包交互时间动态判断,整体算法等同。

4、既然叫退避算法/?那退避的是什么/?

退避,文中原理解释部分退避的是资源冲突;在TCP超时重试中,退避的是超时;在重试中,退避的是导致操作失败的非主观原因;…

附录

大多数人知道退避策略,不知道原理;知道随机退避策略,不知道为什么是随机……

以上是关于你真的会重试吗?——重试机制之二进制指数退避机制的主要内容,如果未能解决你的问题,请参考以下文章

AWS 中的错误重试和指数退避

AWS 中的错误重试和指数退避 Error Retries and Exponential Backoff in AWS

HBase客户端Rpc的重试机制以及客户端参数优化。

RocketMQ高可用设计之消息重试机制

RxJava retryWhen操作符实现错误重试机制

使用Gmail API“500后端错误” - 可以安全重试吗?