前言
负载均衡,英文名称为Load Balance,其含义就是指将负载(工作任务)进行平衡、分摊到多个操作单元上进行运行。
例如:在Dubbo中,同一个服务有多个服务提供者,每个服务提供者所在的机器性能不一致。如果流量均匀分摊,则会导致有些服务提供者负载过高,有些则轻轻松松,导致资源浪费。负载均衡就解决这个问题。
源码
LoadBalance
就是负载均衡的接口,咱们先看看类图
Dubbo提供了4中内置的负载均衡实现:
- RandomLoadBalance:基于权重随机算法
- LeastActiveLoadBalance:基于最少活跃调用数算法
- ConsistentHashLoadBalance:基于 hash 一致性算法
- RoundRobinLoadBalance:基于加权轮询算法
那么负载均衡是在哪里被用的的呢?
AbstractClusterInvoker
的 select
和 reselect
方法。
AbstractLoadBalance
抽象类封装了一些公共的逻辑,在看具体实现类之前,我们先看看抽象类AbstractLoadBalance中的方法
public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) { if (invokers == null || invokers.isEmpty()) return null; // 如果 invokers 列表中仅有一个 Invoker,直接返回即可,无需进行负载均衡 if (invokers.size() == 1) return invokers.get(0); // 调用 doSelect 方法进行负载均衡,该方法为抽象方法,由子类实现 return doSelect(invokers, url, invocation); }
LoadBalance
接口只有一个方法,那就是 select 方法,这是负载均衡的入口。根据 invoker 数量判断是否需要进行负载均衡。这里的 doSelect 是个抽象方法,由子类实现。
protected int getWeight(Invoker<?> invoker, Invocation invocation) { int weight = invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.WEIGHT_KEY, Constants.DEFAULT_WEIGHT); if (weight > 0) { // 获取服务提供者启动时间戳 long timestamp = invoker.getUrl().getParameter(Constants.REMOTE_TIMESTAMP_KEY, 0L); if (timestamp > 0L) { // 计算服务提供者运行时长 int uptime = (int) (System.currentTimeMillis() - timestamp); // 获取服务预热时间,默认为10分钟 int warmup = invoker.getUrl().getParameter(Constants.WARMUP_KEY, Constants.DEFAULT_WARMUP); // 如果服务运行时间小于预热时间,则重新计算服务权重,即降权 if (uptime > 0 && uptime < warmup) { // 重新计算服务权重 weight = calculateWarmupWeight(uptime, warmup, weight); } } } return weight; } static int calculateWarmupWeight(int uptime, int warmup, int weight) { // 计算权重,下面代码逻辑上形似于 (uptime / warmup) * weight。 // 随着服务运行时间 uptime 增大,权重计算值 ww 会慢慢接近配置值 weight int ww = (int) ((float) uptime / ((float) warmup / (float) weight)); return ww < 1 ? 1 : (ww > weight ? weight : ww); }
getWeight 是获取权重的方法,默认权重为100,这里有个服务预热的操作,当服务的启动时间小于预热时间,权重会减少,这个权重由 calculateWarmupWeight 方法计算。
预热的目的是让服务启动后“低功率”运行一段时间,使其效率慢慢提升至最佳状态。
以上就是抽象类的全部方法。下面我们看实现类的。
RandomLoadBalance
RandomLoadBalance 是加权随机算法的具体实现,是Dubbo默认的负载均衡策略。
假设我们有一组服务器 servers = [A, B, C],他们对应的权重为 weights = [5, 3, 2],权重总和为10。
我们取一个大于等于0,小于10的随机数,计算随机数落在哪个区间。例如4在A区间,7在B区间。
权重越大,落在该区间的概率就越大。这就是加权随机算法。
下面看具体代码实现
public class RandomLoadBalance extends AbstractLoadBalance { public static final String NAME = "random"; private final Random random = new Random(); @Override protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) { int length = invokers.size(); // Number of invokers int totalWeight = 0; // The sum of weights boolean sameWeight = true; // Every invoker has the same weight? // 下面这个循环有两个作用,第一是计算总权重 totalWeight, // 第二是检测每个服务提供者的权重是否相同 for (int i = 0; i < length; i++) { int weight = getWeight(invokers.get(i), invocation); totalWeight += weight; // Sum // 检测当前服务提供者的权重与上一个服务提供者的权重是否相同, // 不相同的话,则将 sameWeight 置为 false。 if (sameWeight && i > 0 && weight != getWeight(invokers.get(i - 1), invocation)) { sameWeight = false