轮询状态必须用可变对象保存,不能每次重算;需维护节点的“当前权重”和“已分配次数”,初始current_weight全为0,每轮加原始weight后选最大值,再减总权重和,整数运算最稳妥,推荐封装为线程安全的weightedroundrobin类。

轮询状态必须用可变对象保存,不能每次重算
权重轮询不是简单地按比例重复节点,而是需要维护每个节点的“当前权重”和“已分配次数”状态。如果每次调度都从头计算 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 - 必须在选出节点后才执行
current_weight -= sum_weights,顺序错会导致权重漂移 - 若节点权重为 0,应跳过,否则
current_weight持续负增长,破坏比较逻辑 - 多个节点
current_weight相等时,按索引升序选,保证确定性
权重归一化不是必须的,但不归一化会影响浮点精度和整数溢出
用整数权重(如 3、5、2)直接运算最稳妥;若原始权重是小数(如 0.3、0.5、0.2),必须先放大转整型(如 ×100 → 30、50、20),否则 double 累加会产生误差,几轮后排序错乱。归一化(除以 sum)反而没意义——算法只依赖相对大小,且除法引入额外误差。常见坑:
- 用
float存current_weight:3~4 轮后误差 > 0.01,导致本该第 3 次选的节点被跳过 - 权重和过大(如单节点权重 1e9,共 100 个),
current_weight累加后溢出int32_t,需用int64_t - 动态增删节点时,没重置
current_weight数组长度,访问越界
状态保持要跨调用生命周期,推荐封装成类而非函数静态变量
用 static std::vector<server> state</server> 在函数内维持状态看似简单,但多线程下不安全,且无法支持多个独立调度器实例(比如不同服务集群要用不同权重集)。正确做法是定义 class WeightedRoundRobin,把 std::vector<server></server> 和 sum_weights 作为成员变量。构造时接受 std::vector<int> weights</int> 并完成初始化;提供 next() 方法返回索引或指针。注意:
- 不要在
next()里做深拷贝或字符串拼接,避免隐式性能开销 - 如果需线程安全,对
current_weight更新加std::atomic_int64_t或细粒度互斥锁,而非整个next()加锁 - 调试时可在
next()返回前打印current_weight数组,验证分布是否收敛(例如权重 [3,5,2] 应呈现近似 3:5:2 的长期频率)
权重轮询真正难的不是公式,而是让 current_weight 在整数域里稳定震荡而不漂移——只要一次加法或减法顺序错,后续全部错位。上线前务必用固定权重序列跑 1000 轮,统计各节点出现频次,误差超过 ±5% 就得查状态更新路径。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











