C++实现带权重轮询调度算法 _ 状态保持与权重分布逻辑【源码】
C++实现带权重轮询调度算法 _ 状态保持与权重分布逻辑【源码】

免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈
轮询状态必须用可变对象保存,不能每次重算
这里有个常见的误区:以为权重轮询就是简单地按比例重复节点。其实不然,它的核心在于维护每个节点的“当前权重”和“已分配次数”这两种动态状态。如果每次调度都图省事,从头计算类似 current_weight = weight * (1 - allocated / total_allocated) 这样的公式,历史分配的节奏就全丢了,结果就是在短周期内,请求会严重地向某个节点倾斜,完全违背了平滑分布的初衷。
实际实现中,用 std::vector 来存储节点的元数据(比如 weight、current_weight、index)是成本最低的方案。当然,用 struct Server 封装一下会更清晰,但千万要留神,别顺手把 current_weight 声明成 const 或者只读字段——它可是要频繁变动的。
get_next_server() 的核心逻辑:加权最大值选取 + 状态衰减
标准的实现流程是这样的:每一轮调度,先遍历所有节点,给每个节点的 current_weight 加上它自己的原始 weight。接着,从所有节点里选出 current_weight 最大的那个作为本次结果。最后,也是最关键的一步:将这个被选中节点的 current_weight 减去所有节点的权重总和(sum_weights)。
这个“先加权增、再选最大、最后整体回退”的操作,本质上是在用离散的整数运算,去逼近一个理想的连续分数调度器。有几个细节必须卡死:
current_weight的初始值必须全部设为0,而不是初始化为weight。- “减去总权重和”这个操作,必须在选出节点之后执行,顺序一旦颠倒,整个权重的平衡就会被打破,产生漂移。
- 如果某个节点的权重被设为0,一定要跳过它,否则它的
current_weight会持续负增长,干扰正常的比较逻辑。 - 当多个节点的
current_weight相等时,通常按索引升序选择,以保证结果的确定性。
权重归一化不是必须的,但不归一化会影响浮点精度和整数溢出
直接使用整数权重(比如3、5、2)进行运算是最稳妥的。如果原始权重是小数(例如0.3、0.5、0.2),必须先放大转换成整数(比如乘以100,变成30、50、20)。否则,用 double 类型累加,几轮之后微小的误差就会累积起来,导致排序错乱,该选的节点没选上。反过来,刻意去做归一化(把所有权重除以总和)反而没有意义——算法只依赖权重之间的相对大小,归一化不仅多此一举,还会引入额外的计算误差。
实践中,下面这几个坑经常遇到:
- 用
float存储current_weight:通常3到4轮循环后,累积误差就可能超过0.01,导致本该在第3次被选中的节点意外被跳过。 - 权重和过大:比如单个节点权重就是10亿,总共100个节点,
current_weight累加几次就可能溢出int32_t的范围,这时必须使用int64_t。 - 动态增删节点时,如果忘了同步调整
current_weight状态数组的长度,很容易引发访问越界。
状态保持要跨调用生命周期,推荐封装成类而非函数静态变量
有些偷懒的做法,是在函数内部用一个 static std::vector 来维持状态。这看似简单,却埋下了两个隐患:第一,多线程环境下不安全;第二,它无法支持多个独立的调度器实例(想象一下,你的系统里同时有多个不同的服务集群,每个都需要一套独立的权重配置)。
更专业的做法是定义一个 class WeightedRoundRobin。把 std::vector 和总权重 sum_weights 作为成员变量。构造函数接受一个 std::vector 参数来完成初始化;对外则提供一个 next() 方法来返回选中的节点索引或指针。这里还有几点需要注意:
- 避免在
next()方法内部进行深拷贝或字符串拼接这类操作,防止引入隐性的性能开销。 - 如果需要线程安全,建议对
current_weight的更新操作使用std::atomic_int64_t或更细粒度的互斥锁,而不是简单粗暴地给整个next()方法加上大锁。 - 调试阶段,可以在
next()返回前打印出current_weight数组,观察其变化,验证分布是否平滑收敛。例如,对于权重 [3, 5, 2],长期调用的频率应该稳定在近似3:5:2的比例。
说到底,权重轮询算法真正的难点,不在于理解公式,而在于确保 current_weight 在整数域里能够稳定地“震荡”而不发生“漂移”。只要有一次加法或减法的顺序搞错,后面的所有调度都会错位。所以,在上线之前,务必用一组固定的权重序列,模拟运行个上千轮,然后统计各节点出现的频次。如果误差超过±5%,那就得好好检查一下状态更新的路径是不是哪里出了岔子。
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
如何在 Java 中使用 ExecutorCompletionService 按照异步任务完成的先后顺序获取返回结果
如何在 Ja va 中使用 ExecutorCompletionService 按照异步任务完成的先后顺序获取返回结果 处理异步任务时,你是否遇到过这样的困扰:提交了一堆任务,却只能按照提交顺序一个个等待结果,即便后面的任务先完成了也得干等着?这在处理网络请求或I O操作时尤其低效。好在Ja va并
怎么利用 java.util.Arrays.mismatch() 快速找出两个配置数组中第一个不一致的配置项
如何用 Arrays mismatch() 快速定位配置数组的首个差异项 在配置比对或数据校验的场景里,你是不是也写过循环来逐项比较两个数组?其实,直接用 Arrays mismatch() 就能一步到位,精准锁定第一个差异点的索引。这个方法简直就是为“找不同”量身定制的,不仅代码更简洁,还内置了空
Spring Boot 中实现表单提交下的抽象类多态反序列化
Spring Boot 中实现表单提交下的抽象类多态反序列化 本文介绍如何在 application x-www-form-urlencoded 请求场景下,基于 discriminator 字段动态反序列化为具体子类,绕过 spring 默认无法实例化抽象类的限制。 今天我们来聊聊一个Spring
怎么利用 Maven 的 Profile 功能实现开发、测试与生产环境的配置切换
怎么利用 Ma ven 的 Profile 功能实现开发、测试与生产环境的配置切换 Profile 必须显式用 -P 激活,IDE 不会自动读取 pom xml 里的 activeByDefault 先说一个核心判断:指望 IDE 自动识别 pom xml 里那个 true 标签,这事儿基本不靠谱。
怎么在 Java 中声明并初始化基础数据类型(int, double, boolean)
怎么在 Ja va 中声明并初始化基础数据类型(int, double, boolean) 声明并初始化 int 变量时,别漏掉分号和类型关键字 Ja va 的强类型特性,意味着每个变量都必须有明确的“身份”。int 就是 int,不能像 Ja vaScript 那样用一个 let 或 var 就糊
- 日榜
- 周榜
- 月榜
1
2
3
4
5
6
7
8
9
10
相关攻略
2015-03-10 11:25
2015-03-10 11:05
2021-08-04 13:30
2015-03-10 11:22
2015-03-10 12:39
2022-05-16 18:57
2025-05-23 13:43
2025-05-23 14:01
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程
热门话题

