
本文记录当前负载均衡调用链、现有 LoadBalance 实现行为及相关 git 历史观察。意在分析而非规定:历史记录显示多项权衡,在添加新策略前应保持可见。
负载均衡扩展点是 org.apache.dubbo.rpc.cluster.LoadBalance。它是以 random 为默认实现的 SPI:

• 接口:dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/LoadBalance.java
• SPI 注册:dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.LoadBalance
已注册实现:
RandomLoadBalance
正常消费端调用路径:
1. AbstractClusterInvoker.invoke(Invocation) 收到调用
2. list(invocation) 从目录获取路由候选调用器
3. checkInvokers(invokers, invocation) 无提供者时快速失败
4. initLoadBalance(invokers, invocation) 解析 LoadBalance SPI
5. 具体集群调用器调用 select(loadbalance, invocation, invokers, selected)
6. AbstractClusterInvoker.select(...) 应用粘性选择、可用性检查和重选回退
7. doSelect(...) 调用 loadbalance.select(invokers, getUrl(), invocation)
8. 选中调用器由具体集群策略调用
示例:
• FailfastClusterInvoker#doInvoke(...) 选一次后调用
• FailoverClusterInvoker#doInvoke(...) 每次重试都选,并传已调用列表避免尽可能重试同一提供者
• ForkingClusterInvoker#doInvoke(...) 选多个调用器并行调用
负载均衡名从方法级 URL 参数通过 loadbalance 键读取。若无方法级配置,Dubbo 用默认 random 策略。

策略 | 最适配场景 | 不适配场景 |
|---|---|---|
random | 默认回退、相似容量提供者、低复杂度系统 | 显著延迟或负载差异 |
roundrobin | 稳定提供者、相似请求成本、平滑分布需求 | 慢提供者继续收流量 |
leastactive | 请求成本变化、避当前忙提供者 | 活跃请求数不等于真实响应成本 |
shortestresponse | 响应时间敏感服务、可见延迟波动 | 冷启动行为和失败率信号弱 |
consistenthash | 缓存亲和、租户/会话/键钉住路由 | 热键和动态负载均衡 |
adaptive | 指标反馈可用、动态负载优化需 | 滤器或指标反馈路径不可靠时降级 |
randomRandomLoadBalance 无状态。若无显式权重或预热时间戳,均匀随机选调用器。若有权重或预热活跃,构建累积权重按加权随机选。对大候选列表,用累积权重数组二分搜索。
优点:
• 协调成本极低
• 无每方法或每调用器状态
• 提供者列表变化下健壮
权衡:
• 短窗口分布可不均
• 不响应延迟、活跃请求、失败或提供者负载
roundrobinRoundRobinLoadBalance 用平滑加权轮询。它保持每服务/方法 WeightedRoundRobin 条目映射,按调用器身份键。每次选择加调用器权重到当前值,选最高当前值,然后从选中条目减总权重。
优点:
• 稳定加权分布
• 比随机短窗口平滑更好
权衡:
• 热路径维护可变状态
• 提供者消失需缓存清理
• 不响应延迟或活跃请求数
leastactiveLeastActiveLoadBalance 扫描所有调用器,读调用器 URL 和方法的 RpcStatus.active。选最低活跃数调用器,若权重不同在平局时用加权随机。
优点:
• 适应飞行中请求压力
• 简单,基于现有 RpcStatus 计数器
权衡:
• 活跃数非服务成本
• 忽略成功延迟、失败和超时率
• 需扫描所有候选调用器
shortestresponseShortestResponseLoadBalance 用滑动窗口成功耗时估计响应成本。对每调用器/方法状态估计:
估计响应 = 当前窗口成功平均耗时 * (活跃 + 1)
选最短估计响应,平局用加权随机。
优点:
• 比 leastactive 更延迟感知
• 用滑动窗口而非全历史数据
权衡:
• 估计基于成功调用,失败和超时压力不直接体现
• 冷或无样本节点估计可为零
• 维护每状态窗口偏移并定期重置
consistenthashConsistentHashLoadBalance 构建带虚拟节点的方法级选择器。哈希配置调用参数,在环上找首个匹配虚拟节点,返回该调用器。
优点:
• 保留键亲和
• 对缓存本地性和会话类路由行为有用
权衡:
• 刻意不负载感知
• 热键可过载单提供者
• 选择器缓存和重建行为对并发和路由调用器列表身份敏感
adaptiveAdaptiveLoadBalance 用二选一(P2C):随机采样两调用器,从 AdaptiveMetrics 选较低计算负载者。它还写调用属性和附件,使 AdaptiveLoadBalanceFilter 能在调用后收集响应时间和提供者侧指标。
这意味 adaptive 依赖多于 LoadBalance 实现本身。选择期间 AdaptiveLoadBalance 记录选中提供者、开始时间、loadbalance=adaptive 标记和请求提供者指标(如 mem 和 load)的附件。RPC 完成后 AdaptiveLoadBalanceFilter 检查该标记。成功时读响应附件、加消费端响应时间、更新 AdaptiveMetrics。错误时为选中提供者记录错误请求。
若消费者滤器未激活、响应不携带提供者指标或指标更新路径延迟/丢弃,策略仍运行但丢失部分信号。此时选择可向带不完整/默认指标加权重回退的 P2C 降级,可能不比 random、leastactive 或 shortestresponse 简单策略对该部署更可靠。
优点:
• 比全列表扫描更现代自适应形态
• 用消费端计时和提供者提供指标
• 将权重和超时纳入负载比较
权衡:
• 非单类策略;正确性依赖滤器和指标反馈路径
• 提供者指标尽力且异步
• 策略更多移动部件,更多边界情况
相关 git 历史显示五领域反复工作:平滑、并发、缓存生命周期、方法/参数正确性和热路径成本。
提交 910b261a00(Smooth Round Robin selection (#2650))将旧序列加权轮询替换为当前平滑加权轮询模型。引入每方法 WeightedRoundRoundRobin 状态和加权分布及缓存回收测试。
后提交 317e62f73f(Fix concurrency problems in RpcStatus and RoundRobinLoadBalance (#5881))简化映射更新路径并修复相关并发问题。对新有状态策略是有用警告:选择状态共享、热、易微妙出错。
提交 6d2ba7ec7b(add new loadbalance strategy (#6064))引入 ShortestResponseLoadBalance。初始策略用来自 RpcStatus 的成功平均耗时乘活跃数。
提交 ffb520908a(optmization about shortestResponseLoadBalance (#8441))改变实现保持滑动窗口偏移并定期重置。表明项目已发现全时间平均对负载均衡决策太陈旧。
提交 d17160591c(Adaptive loadbalance (#10745))一起添加 AdaptiveLoadBalance、AdaptiveLoadBalanceFilter 和测试。策略记录选择计时、通过附件请求提供者指标、响应或错误后更新 AdaptiveMetrics。
提交 33f688e404(Update AdaptiveLoadBalance.java (#12636))后修复配对比较中超时查找 bug。凸显自适应策略额外契约表面:调用器身份、超时查找、指标键和监听器行为都重要。

Dubbo 确用 EWMA 风格延迟平滑,但嵌入在 adaptive 策略而非暴露为独立 ewma 负载均衡实现。
AdaptiveMetrics 保持 beta 值和 ewma 字段。提供者指标更新时应用:
Vt = beta * Vt-1 + (1 - beta) * lastLatency
最终自适应负载分数比延迟更广。结合提供者 CPU/负载、EWMA 延迟、飞行中请求、消费端成功比和配置权重。简化形式:
load =
providerCPULoad
* (sqrt(ewma) + 1)
* (inflight + 1)
/ (successRatio * weight + 1)
这意味 Dubbo 当前形态更接近:
adaptive = P2C + 提供者负载 + EWMA 延迟 + 飞行中 + 成功比 + 权重
而非:
ewma = 仅延迟 EWMA
不暴露仅延迟 EWMA 策略作主自适应选择有多个实践原因:
1. 仅延迟 EWMA 可错过重要信号:高飞行中计数、高提供者 CPU/负载、近错误或恢复期低样本置信度
2. EWMA 需响应完成反馈,故不能在 LoadBalance#doSelect(...) 内完全实现。需类似 AdaptiveLoadBalanceFilter 的反馈路径
3. shortestresponse 已用滑动窗口估计覆盖部分响应时间敏感用例
4. 独立 EWMA 策略会引入另一有状态热路径实现,带缓存生命周期、并发、冷启动和方法键隔离关注
结果解读:Dubbo 已采纳 EWMA 作为更广自适应模型一信号,而非将 EWMA 延迟视为完整负载均衡策略。
提交 fee0dbab0c(Improve consistent hashing load balancing with a new algorithm (#8948))尝试向一致性哈希添加过载感知回退:选哈希环节点后,请求数超平均阈值可移到下一节点。
提交 89b74fe7f1(Revert #8948 (#10233))移除该行为。回退是重要背景。一致性哈希价值在键亲和;同策略内加负载重分发改变语义契约。若有界或过载感知一致性哈希变体需,应为显式新策略而非 consistenthash 变更。
提交 caed64b2a6(Fix #13750, ConsistentHashLoadBalance not base on arguments (#13753))修正选择哈希调用参数而非方法名。
提交 c8a8946f89(Fix the issue of high CPU load caused by continuously creating new ConsistentHashSelector instances under high concurrency (#15497))改变选择器创建用原子映射计算。
提交 8f2b4decdc(End the 'read/write' combination operation early to improve performance (#15666))后添加读优先快速路径,使常见情况避免进入 ConcurrentMap.compute(...)。
这些提交共同显示负载均衡代码在极热路径:高并发下不必要分配、映射写或选择器重建变得可见。
历史支持几点谨慎观察,暂不选最终实现方向:
1. 新策略不应静默改现有策略语义。对 consistenthash 尤其如此
2. 热路径成本应视为 API 契约部分。全扫描、compute(...)、分配和锁定需正当理由
3. 仅消费端策略易部署,但只看本地观察。提供者感知策略需类似 adaptive 的反馈路径
4. 状态应小心键化和清理。提供者变化、路由输出变更和方法级隔离都曾导致修复
5. 延迟感知选择应避全时间平均。当前 shortestresponse 历史指向窗口或 EWMA 类衰减
这些是当前实现和历史建议的设计方向,非最终推荐。

添加独立策略,采样两调用器,比较本地消费端信号如:
score = active / max(weight, 1)
或:
score = active * recentAverageRt / max(weight, 1)
这会与 adaptive 中已接受 P2C 形态对齐,但避提供者指标依赖。主问题是仅活跃评分是否足够正当新策略,当 leastactive 已存在。
维护每调用器/方法延迟和失败惩罚指数加权移动平均:
score = ewmaRt * (active + 1) / max(weight, 1) + errorPenalty
这会将 shortestresponse 方向从滑动窗口泛化到连续衰减。需响应完成钩子或复用现有指标路径;仅在 doSelect() 内实现不完整。
添加独立策略如 boundedconsistenthash,保留一致性哈希亲和直到选中节点超显式负载阈值。不应替换 consistenthash,因历史显示改其亲和语义风险。
未来实现应包含测试:
• 确定性或高迭代选择下加权分布
• 提供者增删无过期状态选择
• 共享策略实例上并发选择
• 缓存状态方法级隔离
• 预热权重交互
• 所有指标缺失、零、过期或相等时行为
• 若策略声称失败感知,失败和超时反馈
• 带选择器策略的热路径分配或缓存重建行为