跳到主要内容

退避策略 (backoff)

指数退避加随机抖动的重试延迟计算模块,用于网络请求、设备重连等需要逐步增加重试间隔的场景。

原理

每次调用 backoff_next() 时,先基于当前 delay 计算一个 [0, delay] 范围内的随机值作为返回值,然后再更新 delay(指数增长)。随机抖动可以避免多个客户端同时重试造成"惊群"。

数据结构

struct backoff_t {
int max_delay; /* 最大延迟上限(毫秒) */
int max_retry; /* 最大重试次数,<=0 表示无限制 */
int delay; /* 当前延迟基准值(毫秒) */
int retry; /* 已重试次数 */
};

API

void backoff_init(struct backoff_t * ctx, int delay, int max_delay, int max_retry);

初始化退避上下文。delay 为初始延迟,max_delay 为延迟上限,max_retry 为最大重试次数(<=0 表示无限制)。delay 会被限制在 [0, max_delay] 范围内。

int backoff_next(struct backoff_t * ctx);

获取下一次重试的等待时间(毫秒)。基于当前 delay 计算返回值后再更新 delay。达到最大重试次数后返回 -1。

增长策略

每次调用 backoff_next() 后,delay 按以下规则更新:

条件更新方式
delay < max_delay / 2delay += max(delay, 1)(当 delay >= 1 时等价于翻倍)
delay >= max_delay / 2delay = max_delay(封顶)

其中阈值 max_delay / 2 为整数右移计算(max_delay >> 1),例如 max_delay = 1000 时阈值为 500。

演变示例

backoff_init(&bo, 10, 1000, 10) 为例,delay 的演变过程:

调用次序当前 delay返回范围更新后 delay说明
110[0, 10]2010 < 500 → 10+10=20
220[0, 20]4020 < 500 → 20+20=40
340[0, 40]8040 < 500 → 40+40=80
480[0, 80]16080 < 500 → 80+80=160
5160[0, 160]320160 < 500 → 160+160=320
6320[0, 320]640320 < 500 → 320+320=640
7640[0, 640]1000640 >= 500 → 封顶
81000[0, 1000]1000已封顶
91000[0, 1000]1000已封顶
101000[0, 1000]1000已封顶
11-返回 -1-重试耗尽

实际运行输出(返回值为随机抖动结果):

[0] next = 1
[1] next = 2
[2] next = 14
[3] next = 30
[4] next = 16
[5] next = 182
[6] next = 265
[7] next = 250
[8] next = 352
[9] next = 308
[10] next = -1

使用示例

网络重连

struct backoff_t bo;
backoff_init(&bo, 100, 30000, 10);

int wait;
while((wait = backoff_next(&bo)) >= 0)
{
if(try_connect() == SUCCESS)
break;
xos_msleep(wait);
}
if(wait < 0)
/* 重试耗尽 */

无限重试

struct backoff_t bo;
backoff_init(&bo, 50, 5000, 0); /* max_retry<=0, 无限制 */

int wait;
while(1)
{
if(try_connect() == SUCCESS)
break;
wait = backoff_next(&bo);
xos_msleep(wait);
}