本文基于dubbo v2.6.x
1.负载均衡
本文我们将解析一下dubbo 集群的负载均衡的接口部分,dubbo的负载均衡属于客户端负载均衡,就是在调用端做的负载,比如一个接口有多个服务提供者实例提供服务,然后我们服务调用端在调用的时候需要选择一个合适的服务提供者实例来调用。这个选择的过程就是负载均衡算法。
2.dubbo负载均衡接口
我们来看下dubbo对负载均衡提供的抽象接口LoadBalance(如果我们要在dubbo中使用自己的负载均衡算法,只需要实现该接口,重写其方法,然后再根据dubbo spi 配置好实现,在使用的时候在服务调用者端配置loadbalance属性告诉dubbo使用哪种负载均衡算法。
@SPI(RandomLoadBalance.NAME)
public interface LoadBalance {
/**
* select one invoker in list.
*
* @param invokers invokers.
* @param url refer url
* @param invocation invocation.
* @return selected invoker.
*/
@Adaptive("loadbalance")
<T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
}
可以看到就定义了一个select 方法,我们来解释下这几个参数
invokers:服务提供者们
url: 服务引用url
invocation: 本次调用信息
我们负载均衡就是根据某种算法从invokers 里面选择一个合适的invoker。
3. dubbo负载均衡实现
在dubbo v2.6.x版本中,dubbo官方给我提供了4中线上使用的负载均衡算法。
我们可以看到 AbstractLoadBalance 抽象类实现LoadBalance 接口,然后下面这个四个继承AbstractLoadBalance 抽象类,这个MockLoadBalance我们不用管。
解释下这几个实现类大体的功能:
- AbstractLoadBalance:抽象类,实现LoadBalance ,重写select方法,处理invokers是空或者单个invoker的情况, 抽象doSelect方法处理多invoker的情况让子类实现,同时提供计算权重的方法。
- RandomLoadBalance : 继承AbstractLoadBalance 抽象类,重写doSelect 方法,随机在invokers里面选择一个invoker,不过这个选择是依据各个invoker权重的。
- RoundRobinLoadBalance : 继承AbstractLoadBalance 抽象类,重写doSelect 方法 ,使用轮询的方式选择invoker,也是受invoker权重影响。
- LeastActiveLoadBalance: 最少活跃数,继承AbstractLoadBalance 抽象类,重写doSelect 方法,选择一个干活最少的invoker。
- ConsistentHashLoadBalance: 继承AbstractLoadBalance 抽象类,重写doSelect 方法 ,使用一致性hash算法选择invoker。
4. AbstractLoadBalance 源码
我们来看下AbstractLoadBalance的源码:
public abstract class AbstractLoadBalance implements LoadBalance {
/**
*
* @param uptime 服务起启动时间
* @param warmup 服务正常提供服务一个缓冲时间,也就是我这个服务器预热,默认预热10分钟
* @param weight 初始权重(这个是用户自己设置,也可以是默认的100)
* @return 计算后的权重
*/
static int calculateWarmupWeight(int uptime, int warmup, int weight) {
// 服务器启动时间 / (服务器预热需要的时间/初始权重)
int ww = (int) ((float) uptime / ((float) warmup / (float) weight));
// 计算完成的权重 小于1的时候就是1 , 大于1的时候 与初始阶段的权重做比较,大于初始阶段 就选初始的那个权重,小于的话就选计算后的权重
return ww < 1 ? 1 : (ww > weight ? weight : ww);
}
/**
* 在invokers选择一个合适的invoker, 这里如果服务提供者们 就一个,直接返回那一个。然后再交由子类doSelect(invokers, url, invocation);进行选择。
* @param invokers invokers. 服务提供者们
* @param url refer url 引用url
* @param invocation invocation. 调用信息
* @param <T>
* @return 选择出来的那个invoker
*/
@Override
public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {
if (invokers == null || invokers.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);
// 获取权重信息
protected int getWeight(Invoker<?> invoker, Invocation invocation) {
// 缺省权重 是100
int weight = invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.WEIGHT_KEY, Constants.DEFAULT_WEIGHT);
if (weight > 0) {
/// 获取remote.timestamp 属性值 缺省是0
long timestamp = invoker.getUrl().getParameter(Constants.REMOTE_TIMESTAMP_KEY, 0L);
if (timestamp > 0L) {
// 计算invoker 对应的这个服务提供者服务 服务时间(就是从启动到现在)
int uptime = (int) (System.currentTimeMillis() - timestamp);
//获取 warmup 属性值 ,缺省是10分钟
int warmup = invoker.getUrl().getParameter(Constants.WARMUP_KEY, Constants.DEFAULT_WARMUP);
if (uptime > 0 && uptime < warmup) {
//如果机器启动时间小于这个10分钟,就进行计算这个权重值,
// 如果是大于这个10分钟,然后权重就是你设置的那个,没有设置就是100
// 计算权重值
weight = calculateWarmupWeight(uptime, warmup, weight);
}
}
}
return weight;
}
}
我们可以看到它实现了LoadBalance的select 方法,在该方法中处理了invokers 为空或者只存在一个元素的情况,多元素的情况交由给子类实现doSelect方法。
同时它还提供了getWeight 计算权重的方法,用户可以在服务提供者端使用weight来配置权重(缺省权重为100),也可以配置服务的预热时间,通过服务提供者服务运行时间 与 服务预热需要的时间 进行比较,如果服务还在预热范围内,然后就计算权重,公式 = 服务器启动时间 / (服务器预热需要的时间/初始权重),如果得到的权重小于1,就返回1,计算出来的权重大于之前配置的权重,使用之前的权重。
5. 总结
我们本篇主要是介绍了dubbo负载均衡接口,在dubbo v2.6.x提供的实现以及AbstractLoadBalance 抽象类源码解析, 每个实现类大体的介绍。