2.源码分析

在 Dubbo 中,所有负载均衡实现类均继承自 AbstractLoadBalance,该类实现了 LoadBalance 接口,并封装了一些公共的逻辑。所以在分析负载均衡实现之前,先来看一下 AbstractLoadBalance 的逻辑。首先来看一下负载均衡的入口方法 select,如下:

  1. @Override
  2. public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {
  3. if (invokers == null || invokers.isEmpty())
  4. return null;
  5. // 如果 invokers 列表中仅有一个 Invoker,直接返回即可,无需进行负载均衡
  6. if (invokers.size() == 1)
  7. return invokers.get(0);
  8. // 调用 doSelect 方法进行负载均衡,该方法为抽象方法,由子类实现
  9. return doSelect(invokers, url, invocation);
  10. }
  11. protected abstract <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation);

select 方法的逻辑比较简单,首先会检测 invokers 集合的合法性,然后再检测 invokers 集合元素数量。如果只包含一个 Invoker,直接返回该 Inovker 即可。如果包含多个 Invoker,此时需要通过负载均衡算法选择一个 Invoker。具体的负载均衡算法由子类实现,接下来章节会对这些子类一一进行详细分析。

AbstractLoadBalance 除了实现了 LoadBalance 接口方法,还封装了一些公共逻辑,比如服务提供者权重计算逻辑。具体实现如下:

  1. protected int getWeight(Invoker<?> invoker, Invocation invocation) {
  2. // 从 url 中获取权重 weight 配置值
  3. int weight = invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.WEIGHT_KEY, Constants.DEFAULT_WEIGHT);
  4. if (weight > 0) {
  5. // 获取服务提供者启动时间戳
  6. long timestamp = invoker.getUrl().getParameter(Constants.REMOTE_TIMESTAMP_KEY, 0L);
  7. if (timestamp > 0L) {
  8. // 计算服务提供者运行时长
  9. int uptime = (int) (System.currentTimeMillis() - timestamp);
  10. // 获取服务预热时间,默认为10分钟
  11. int warmup = invoker.getUrl().getParameter(Constants.WARMUP_KEY, Constants.DEFAULT_WARMUP);
  12. // 如果服务运行时间小于预热时间,则重新计算服务权重,即降权
  13. if (uptime > 0 && uptime < warmup) {
  14. // 重新计算服务权重
  15. weight = calculateWarmupWeight(uptime, warmup, weight);
  16. }
  17. }
  18. }
  19. return weight;
  20. }
  21. static int calculateWarmupWeight(int uptime, int warmup, int weight) {
  22. // 计算权重,下面代码逻辑上形似于 (uptime / warmup) * weight。
  23. // 随着服务运行时间 uptime 增大,权重计算值 ww 会慢慢接近配置值 weight
  24. int ww = (int) ((float) uptime / ((float) warmup / (float) weight));
  25. return ww < 1 ? 1 : (ww > weight ? weight : ww);
  26. }

上面是权重的计算过程,该过程主要用于保证当服务运行时长小于服务预热时间时,对服务进行降权,避免让服务在启动之初就处于高负载状态。服务预热是一个优化手段,与此类似的还有 JVM 预热。主要目的是让服务启动后“低功率”运行一段时间,使其效率慢慢提升至最佳状态。

关于 AbstractLoadBalance 就先分析到这,接下来分析各个实现类的代码。首先,我们从 Dubbo 缺省的实现类 RandomLoadBalance 看起。