目录
- 五种负载均衡策略
- LoadBalance 接口
- `RandomLoadBalance` 随机访问策略
- 自定义的负载均衡的使用方式?
- 需要自己实现负载均衡类
- 增加SPI配置文件
- 在@Reference 注解上配置均衡策略
五种负载均衡策略
Dubbo 最新的源码中可以看到提供了以下五种策略:
-
RandomLoadBalance
: 随机访问策略 -
LeastActiveLoadBalance
: 最少访问策略 -
ConsistentHashLoadBalance
: 一致性hash访问策略 -
RoundRobinLoadBalance
: 轮流访问策略 -
ShortestResponseLoadBalance
:最快响应策略(就是调用响应最快的服务提供者)
LoadBalance 接口
这五个策略都实现了实现了AbstractLoadBalance
抽象类,而AbstractLoadBalance
抽象类都实现了LoadBalance
接口。 而LoadBalance 接口就是Dubbo中的SPI 接口,看下接口定义:
可以看到默认使用的是随机访问策略, 并且定义了一个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));
}
从上面的代码看到, 它的随机也不是平均随机的, 还是使用权重来确定随机的概率的。
具体的随机算法就是流程简述如下:
- 计算出每个服务提供者的权重, 具体的计算是调用这个
getWeight
方法的。 在这个方法内部逻辑是从URL中获取权重的。 - 权重求和, 获取总权重
totalWeight
- 获取0到
totalWeight
之间的一个随机数offset
- 遍历提供者, 使用随机数
offset
减去每个提供者的权重, 直到减去后的值小于0 才会返回这个提供者
这四步就实现了一个有概率的随机算法, 每个调用者的概率是根据其权重计算出来的。 如果权重一样, 或者都没有设置权重,才会概率一样的调用提供者。每个调用者的权重还会随着时间来增长的, 所以长时间运行的提供者的被随机到的概率是大些的。
可以看到doSelect的作用就是返回一个提供者。
自定义的负载均衡的使用方式?
需要自己实现负载均衡类
实现的逻辑大概如下:
- 继承AbstractLoadBalance类, 重写doSelect方法。
- 从Url中获取到调用的方法名称,如果不是我们需要自定义的方法就获取默认的RandomLoadBalance对象来处理逻辑。
- 如果是我们需要处理的方法就进入我们的逻辑, 从invocation中获取到请求的参数, 找到你要的参数, 对其进行hash,之后简单的取模确定提供者。 再返回具体提供者
增加SPI配置文件
- 在项目的META-INF/dubbo 目录下增加配置文件, 文件名就是这个:
org.apache.dubbo.rpc.cluster.LoadBalance
- 文件的内容是key-value的形式: 如:
myLoadBalance = com.my.dubbo.loadbalance.MyLoadBalance
其中的value值就是你实现的自定义的负载均衡类
在@Reference 注解上配置均衡策略
- 在引入dubbo消费者的@Reference注解上配置好负载策略
- Reference注解中的loadbalance属性配置成myLoadBalance , 比如:
@Reference(loadbalance = "myLoadBalance")
private RemoteService remoteService;
配置好后对于这个服务的调用都会使用你自己定义的负载均衡策略。
上面的流程就是使用自定义负载均衡的流程, 使用十分简单方便, 这是因为dubbo本身就是一个方便扩展的框架。
至于为何这样配置就能生效, 这设计到dubbo的SPI机制和Reference注解的机制。 我会留在后面在补充完整。