目录

  • 五种负载均衡策略
  • LoadBalance 接口
  • `RandomLoadBalance` 随机访问策略
  • 自定义的负载均衡的使用方式?
  • 需要自己实现负载均衡类
  • 增加SPI配置文件
  • 在@Reference 注解上配置均衡策略


五种负载均衡策略

Dubbo 最新的源码中可以看到提供了以下五种策略:

  • RandomLoadBalance: 随机访问策略
  • LeastActiveLoadBalance: 最少访问策略
  • ConsistentHashLoadBalance: 一致性hash访问策略
  • RoundRobinLoadBalance: 轮流访问策略
  • ShortestResponseLoadBalance:最快响应策略(就是调用响应最快的服务提供者)

LoadBalance 接口

这五个策略都实现了实现了AbstractLoadBalance抽象类,而AbstractLoadBalance抽象类都实现了LoadBalance接口。 而LoadBalance 接口就是Dubbo中的SPI 接口,看下接口定义:

dubbo如何实现负载均衡和熔断_dubbo如何实现负载均衡和熔断


可以看到默认使用的是随机访问策略, 并且定义了一个select接口, 我们看下RandomLoadBalance的源码。

RandomLoadBalance 随机访问策略

我们想看下抽象类AbstractLoadBalance, 这个类实现了select方法, 并且留下了抽象方法doSelect才是五种策略的差异:

@Override
    public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {
        if (CollectionUtils.isEmpty(invokers)) {
            return null;
        }
        //只有一个调用者, 不用讨论什么策略了
        if (invokers.size() == 1) {
            return invokers.get(0);
        }
        //调用模板方法,模板方法由不同的子类实现
        return doSelect(invokers, url, invocation);
    }

    protected abstract <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation);

看下RandomLoadBalance的doselect的方法实现:

/**
    * Select one invoker between a list using a random criteria
    * @param invokers List of possible invokers
    * @param url URL
    * @param invocation Invocation
    * @param <T>
    * @return The selected invoker
    */
   @Override
   protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
       // Number of invokers
       int length = invokers.size();
       // Every invoker has the same weight?
       boolean sameWeight = true;
       // the weight of every invokers
       int[] weights = new int[length];
       // the first invoker's weight
       int firstWeight = getWeight(invokers.get(0), invocation);
       weights[0] = firstWeight;
       // The sum of weights
       int totalWeight = firstWeight;
       for (int i = 1; i < length; i++) {
           int weight = getWeight(invokers.get(i), invocation);
           // save for later use
           weights[i] = weight;
           // Sum
           totalWeight += weight;
           if (sameWeight && weight != firstWeight) {
               sameWeight = false;
           }
       }
       if (totalWeight > 0 && !sameWeight) {
           // If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on totalWeight.
           int offset = ThreadLocalRandom.current().nextInt(totalWeight);
           // Return a invoker based on the random value.
           for (int i = 0; i < length; i++) {
               offset -= weights[i];
               if (offset < 0) {
                   return invokers.get(i);
               }
           }
       }
       // If all invokers have the same weight value or totalWeight=0, return evenly.
       return invokers.get(ThreadLocalRandom.current().nextInt(length));
   }

从上面的代码看到, 它的随机也不是平均随机的, 还是使用权重来确定随机的概率的。
具体的随机算法就是流程简述如下:

  1. 计算出每个服务提供者的权重, 具体的计算是调用这个getWeight方法的。 在这个方法内部逻辑是从URL中获取权重的。
  2. 权重求和, 获取总权重totalWeight
  3. 获取0到totalWeight之间的一个随机数 offset
  4. 遍历提供者, 使用随机数offset减去每个提供者的权重, 直到减去后的值小于0 才会返回这个提供者

这四步就实现了一个有概率的随机算法, 每个调用者的概率是根据其权重计算出来的。 如果权重一样, 或者都没有设置权重,才会概率一样的调用提供者。每个调用者的权重还会随着时间来增长的, 所以长时间运行的提供者的被随机到的概率是大些的。

可以看到doSelect的作用就是返回一个提供者。

自定义的负载均衡的使用方式?

需要自己实现负载均衡类

实现的逻辑大概如下:

  1. 继承AbstractLoadBalance类, 重写doSelect方法。
  2. 从Url中获取到调用的方法名称,如果不是我们需要自定义的方法就获取默认的RandomLoadBalance对象来处理逻辑。
  3. 如果是我们需要处理的方法就进入我们的逻辑, 从invocation中获取到请求的参数, 找到你要的参数, 对其进行hash,之后简单的取模确定提供者。 再返回具体提供者

增加SPI配置文件

  1. 在项目的META-INF/dubbo 目录下增加配置文件, 文件名就是这个:org.apache.dubbo.rpc.cluster.LoadBalance
  2. 文件的内容是key-value的形式: 如:
myLoadBalance = com.my.dubbo.loadbalance.MyLoadBalance

其中的value值就是你实现的自定义的负载均衡类

在@Reference 注解上配置均衡策略

  1. 在引入dubbo消费者的@Reference注解上配置好负载策略
  2. Reference注解中的loadbalance属性配置成myLoadBalance , 比如:
@Reference(loadbalance = "myLoadBalance")
    private  RemoteService remoteService;

配置好后对于这个服务的调用都会使用你自己定义的负载均衡策略。

上面的流程就是使用自定义负载均衡的流程, 使用十分简单方便, 这是因为dubbo本身就是一个方便扩展的框架。

至于为何这样配置就能生效, 这设计到dubbo的SPI机制和Reference注解的机制。 我会留在后面在补充完整。