FreeRTOS 退避算法

backoffAlgorithm 核心算法详解

目录

  1. 算法概述
  2. 数据结构分析
  3. 核心算法逻辑
  4. 代码逐行解析
  5. 算法示例演示
  6. 算法特性分析
  7. 使用场景和最佳实践

算法概述

什么是退避算法(Backoff Algorithm)?

退避算法是一种用于处理失败重试的策略,通过逐渐增加重试之间的等待时间,避免在系统繁忙或网络拥塞时造成"雷群效应"(Thundering Herd Problem)。

Full Jitter 策略

backoffAlgorithm 库实现了 “Full Jitter” 指数退避策略,这是 AWS 推荐的一种退避算法变体。

核心思想:

  • 指数增长:每次重试的等待时间上限呈指数增长(2^n)
  • 随机抖动:在每次重试时,实际等待时间是在 [0, 最大等待时间] 范围内随机选择
  • 避免同步:多个设备同时重试时,由于随机性,重试时间会分散,避免同时冲击服务器

算法公式

第 n 次重试的等待时间 = random(0, min(backOffBase * 2^n, maxBackoffDelay)) 

其中:

  • backOffBase:基础退避时间(毫秒)
  • 2^n:指数增长因子
  • maxBackoffDelay:最大退避延迟(毫秒)
  • random(0, max):在 [0, max] 范围内随机选择

数据结构分析

BackoffAlgorithmContext_t 结构体

typedefstructBackoffAlgorithmContext{ uint16_t maxBackoffDelay;// 最大退避延迟(毫秒)uint32_t attemptsDone;// 已完成的尝试次数uint16_t nextJitterMax;// 下一次重试的最大抖动值(毫秒)uint32_t maxRetryAttempts;// 最大重试次数} BackoffAlgorithmContext_t;

字段说明:

字段类型说明
maxBackoffDelayuint16_t最大退避延迟(毫秒)。这是退避时间的上限,无论重试多少次,都不会超过这个值
attemptsDoneuint32_t已完成的尝试次数。每次调用 GetNextBackoff 时递增
nextJitterMaxuint16_t下一次重试的最大抖动值(毫秒)。这是当前重试周期中随机等待时间的上限,会指数增长
maxRetryAttemptsuint32_t最大重试次数。设置为 BACKOFF_ALGORITHM_RETRY_FOREVER (UINT32_MAX) 表示无限重试

关键常量:

#defineBACKOFF_ALGORITHM_RETRY_FOREVER( UINT32_MAX )

核心算法逻辑

1. 初始化函数:BackoffAlgorithm_InitializeParams

函数签名:

voidBackoffAlgorithm_InitializeParams( BackoffAlgorithmContext_t * pContext,uint16_t backOffBase,uint16_t maxBackOff,uint32_t maxAttempts );

参数说明:

参数类型说明
pContextBackoffAlgorithmContext_t*要初始化的上下文结构体指针
backOffBaseuint16_t基础退避时间(毫秒)。第一次重试的等待时间上限
maxBackOffuint16_t最大退避延迟(毫秒)。所有重试的等待时间上限
maxAttemptsuint32_t最大重试次数。设置为 BACKOFF_ALGORITHM_RETRY_FOREVER 表示无限重试

实现代码:

voidBackoffAlgorithm_InitializeParams( BackoffAlgorithmContext_t * pContext,uint16_t backOffBase,uint16_t maxBackOff,uint32_t maxAttempts ){ assert( pContext !=NULL);/* 初始化上下文,设置计算下一次退避值所需的参数 */ pContext->nextJitterMax = backOffBase;// 第一次重试的最大抖动值 = 基础值 pContext->maxBackoffDelay = maxBackOff;// 设置最大退避延迟 pContext->maxRetryAttempts = maxAttempts;// 设置最大重试次数/* 初始化时,已完成的尝试次数为 0 */ pContext->attemptsDone =0;}

初始化逻辑:

  1. nextJitterMax 设置为 backOffBase(第一次重试的等待时间上限)
  2. 设置 maxBackoffDelay(所有重试的等待时间上限)
  3. 设置 maxRetryAttempts(最大重试次数)
  4. attemptsDone 初始化为 0

2. 核心算法函数:BackoffAlgorithm_GetNextBackoff

函数签名:

BackoffAlgorithmStatus_t BackoffAlgorithm_GetNextBackoff( BackoffAlgorithmContext_t * pRetryContext,uint32_t randomValue,uint16_t* pNextBackOff );

参数说明:

参数类型说明
pRetryContextBackoffAlgorithmContext_t*退避算法上下文(输入/输出)
randomValueuint32_t随机值,范围 [0, UINT32_MAX],用于计算随机等待时间
pNextBackOffuint16_t*输出参数,返回计算出的退避时间(毫秒)

返回值:

返回值说明
BackoffAlgorithmSuccess成功计算出下一个退避值
BackoffAlgorithmRetriesExhausted所有重试次数已用完

实现代码:

BackoffAlgorithmStatus_t BackoffAlgorithm_GetNextBackoff( BackoffAlgorithmContext_t * pRetryContext,uint32_t randomValue,uint16_t* pNextBackOff ){  BackoffAlgorithmStatus_t status = BackoffAlgorithmSuccess;assert( pRetryContext !=NULL);assert( pNextBackOff !=NULL);/* 如果 maxRetryAttempts 设置为最大值,或者已完成次数小于最大次数,则继续重试 */if(( pRetryContext->maxRetryAttempts == BACKOFF_ALGORITHM_RETRY_FOREVER )||( pRetryContext

Read more

数据结构:顺序表讲解(1)

数据结构:顺序表讲解(1)

目录 前言  一、顺序表介绍 介绍: 1.线性表 线性表:逻辑结构的统称 2.顺序表概念与结构 二、顺序表分类 介绍: 1.静态顺序表 2.动态顺序表 核心特点 三、动态顺序表的实现 讲解 1.初始化: SLinit 2.顺序表的尾插 3.顺序表的头插 4.顺序表的尾删 5.顺序表的头删 四、尾插,头插,尾删,头删时间复杂度对比: 1.尾插入: 2.头插入: 3.尾删: 4.头删:    总结 前言    本篇文章将讲解顺序表介绍,顺序表分类,

By Ne0inhk
惊叹数据结构之美,品味排序算法之妙:对计排、桶排的详细介绍

惊叹数据结构之美,品味排序算法之妙:对计排、桶排的详细介绍

大家好,这里是小编的博客频道 小编的博客:就爱学编程 很高兴在ZEEKLOG这个大家庭与大家相识,希望能在这里与大家共同进步,共同收获更好的自己!!! 本文目录 * 引言 * 正文 * 一、计数排序(Counting Sort) * 二、基数排序(Radix Sort) * 三、总结 * 快乐的时光总是短暂,咱们下篇博文再见啦!!!不要忘了,给小编点点赞和收藏支持一下,在此非常感谢!!! 引言 排序算法中的基数排序和计数排序都是非基于传统比较的排序方法,它们各自有着独特的实现原理和应用场景。下面小编将从代码实现的角度对这两种排序算法进行详细介绍。 那接下来就让我们开始遨游在知识的海洋! 正文 一、计数排序(Counting Sort) 原理概述: 计数排序是一种适用于元素范围较小的排序算法。它利用一个额外的计数数组来记录待排序数组中每个元素出现的次数,然后根据这些次数来确定每个元素在最终排序数组中的位置。 代码实现步骤: 1. 确定元素范围:找出待排序数组中的最小值和最大值,记为min和max。2. 创建计数数组:创建

By Ne0inhk
【C++进阶系列】:万字详解unordered_set和unordered_map,带你手搓一个哈希表!(附模拟实现unordered_set和unordered_map的源码)

【C++进阶系列】:万字详解unordered_set和unordered_map,带你手搓一个哈希表!(附模拟实现unordered_set和unordered_map的源码)

🔥 本文专栏:c++ 🌸作者主页:努力努力再努力wz 💪 今日博客励志语录:努力不是为了回报,而是不让自己留下任何遗憾 ★★★ 本文前置知识: map和set模拟实现 引入 那么在正式讲解STL的unordered_map以及unordered_set这两个容器之前,我们先来回顾一下,目前我们接触到能够高效查找数据的数据结构,那么首先我们可以想到的能够实现高效查找数据的数据结构便是数组,但是这里的数组不是简单的将元素直接存放到数组中的任意位置,而是会将存储在数组中的元素先进行一次排序,然后借助二分算法来进行查找,由于这里数组的排序只需要一次,那么排序付出的代价可以均摊到每一次的查找操作中,所以这里排序的代价可以忽略不计,而二分查找的时间复杂度则是logN,所以这种方式能够实现高效的数据查找,但是如果涉及到插入以及删除操作的话,如果插入以及删除元素不在数组末尾,那么必然就要移动大量的元素,意味着插入和删除的时间复杂度最坏情况下会到达O(N),效率相比于查找就不那么高效 接着就是在二叉搜索树的基础上优化,压缩其高度的AVL树和红黑树这两个数据结构,这两种数据结

By Ne0inhk
【LeetCode原地复写零】:双指针+逆向填充,O(n)时间O(1)空间最优解!

【LeetCode原地复写零】:双指针+逆向填充,O(n)时间O(1)空间最优解!

🎁个人主页:User_芊芊君子 🎉欢迎大家点赞👍评论📝收藏⭐文章 🔍系列专栏:Java.数据结构 【前言】 本文聚焦 LeetCode“原地复写零”经典题目,核心需求是在固定长度数组中复写每个 0并右移其余元素,且需满足原地修改、不使用额外数组空间的约束。正向遍历易导致后续元素被覆盖,为此本文详解双指针+逆向填充的优雅解法,高效破解这一核心难点。 文章目录: * 一、复写零 * 二、思路分析 * 1.找到复写的最后一个数 * 2.开始从后往前复写 * 三、代码展示 * 四、时间和空间复杂度分析 * 五、总结 一、复写零 二、思路分析 复写零这道题是让在原数组修改,如果从前向后遍历,后面的元素会被覆盖,所以我们要找到被复写的最后一个元素,然后从后往前复写。运用双指针+逆向填充 1.

By Ne0inhk