前言

负载均衡是指在集群中,将多个数据请求分散在不同单元上进行执行,主要为了提高系统容错能力和加强系统对数据的处理能力。

在 Dubbo 中,一次服务的调用就是对所有实体域 Invoker 的一次筛选过滤,最终选定具体调用的 Invoker。首先在 Directory 中获取全部 Invoker 列表,通过路由筛选出符合规则的 Invoker,最后再经过负载均衡选出具体的 Invoker。所以 Dubbo 负载均衡机制是决定一次服务调用使用哪个提供者的服务。

整体结构

Dubbo 负载均衡的分析入口是 org.apache.dubbo.rpc.cluster.loadbalance.AbstractLoadBalance 抽象类,查看这个类继承关系。

这个被 RandomLoadBalance、LeastActiveLoadBalance、RoundRobinLoadBalance 及 ConsistentHashLoadBalance 类继承,这四个类是 Dubbo 中提供的四种负载均衡算法的实现。

名称 说明
RandomLoadBalance 随机算法,根据权重设置随机的概率
LeastActiveLoadBalance

最少活跃数算法,指请求数和完成数之差,使执行效率高的服务接收更多请求

RoundRobinLoadBalance 加权轮训算法,根据权重设置轮训比例
ConsistentHashLoadBalance

Hash 一致性算法,相同请求参数分配到相同提供者

以上则是 Dubbo 提供的四种负载均衡算法。

从上图中,看到 AbstractLoadBalance 实现了 LoadBalance 接口,同时是一个 SPI 接口,指定默认实现为 RandomLoadBalance 随机算法机制。

抽象类 AbstractLoadBalance 中,实现了负载均衡通用的逻辑,同时给子类声明了一个抽象方法供子类实现其负载均衡的逻辑。

public abstract class AbstractLoadBalance implements LoadBalance {/**** @param 运行时间(毫秒)* @param 预热时间(毫秒)* @param 要计算的 Invoker 权重值*/static int calculateWarmupWeight(int uptime, int warmup, int weight) {// 计算预热时期的权重int ww = (int) ((float) uptime / ((float) warmup / (float) weight));// 返回的权重值区间在: 1 ~ weightreturn ww < 1 ? 1 : (ww > weight ? weight : ww);}@Overridepublic <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {// 校验 invokers 是否为空if (CollectionUtils.isEmpty(invokers)) {return null;}// 当到达负载均衡流程时,invokers 中只有一个 Invoker 时,直接返回该 Invokerif (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) {// 获取当前Invoker配置的权重值int weight = invoker.getUrl().getMethodParameter(invocation.getMethodName(), WEIGHT_KEY, DEFAULT_WEIGHT);if (weight > 0) {// 服务启动时间long timestamp = invoker.getUrl().getParameter(REMOTE_TIMESTAMP_KEY, 0L);if (timestamp > 0L) {// 服务已运行时长int uptime = (int) (System.currentTimeMillis() - timestamp);// 服务预热时间,默认 DEFAULT_WARMUP = 10 * 60 * 1000 ,预热十分钟int warmup = invoker.getUrl().getParameter(WARMUP_KEY, DEFAULT_WARMUP);// 如果服务运行时长小于预热时长,重新计算出预热时期的权重if (uptime > 0 && uptime < warmup) {weight = calculateWarmupWeight(uptime, warmup, weight);}}}// 保证最后返回的权重值不小于0return weight >= 0 ? weight : 0;}
}

在 AbstractLoadBalance 中,getWeight 和 calculateWarmupWeight 方法是获取和计算当前 Invoker 的权重值。

getWeight 中获取当前权重值,通过 URL 获取当前 Invoker 设置的权重,如果当前服务提供者启动时间小于预热时间,则会重新计算权重值,对服务进行降权处理,保证服务能在启动初期不分发设置比例的全部流量,健康运行下去。

calculateWarmupWeight 是重新计算权重值的方法,计算公式为: 服务运行时长/(预热时长/设置的权重值),等价于 (服务运行时长/预热时长)*设置的权重值,同时条件 服务运行时长<预热时长。由该公式可知,预热时长和设置的权重值不变,服务运行时间越长,计算出的值越接近 weight,但不会等于 weight。在返回计算后的权重结果中,对小于1和大于设置的权重值进行了处理,当重新计算后的权重小于1时返回1;处于1和设置的权重值之间时,直接返回计算后的结果;当权重大于设置的权重值时(因为条件限制,不会出现该类情况),返回设置的权重值。所以得出结论:重新计算后的权重值为 1 ~ 设置的权重值,运行时间越长,计算出的权重值越接近设置的权重值

配置方式

服务端

通过 XML 配置方式:

<!-- 服务级别配置 -->
<dubbo:service id="xXXXService" interface="top.ytao.service.XXXXService" class="top.ytao.service.impl.XXXXServiceImpl" loadbalance="负载策略" />
<!-- 方法级别配置 -->
<dubbo:service id="xXXXService" interface="top.ytao.service.XXXXService" class="top.ytao.service.impl.XXXXServiceImpl"><dubbo:method name="方法名" loadbalance="负载策略"/>
</dubbo:service>

通过 Properties 配置:

dubbo.service.loadbalance=负载策略

通过注解方式:

@Service(loadbalance = "负载策略")

客户端

通过 XML 配置方式:

<!-- 服务级别配置 -->
<dubbo:reference id="xXXXService" interface="top.ytao.service.XXXXService" loadbalance="负载策略" />
<!-- 方法级别配置 -->
<dubbo:reference id="xXXXService" interface="top.ytao.service.XXXXService"><dubbo:method name="方法名" loadbalance="负载策略"/>
</dubbo:reference>

通过 Properties 配置:

dubbo.reference.loadbalance=负载策略

通过注解配置方式:

@Reference(loadbalance = "负载策略")

实现方式也可通过 Dubbo-Admin 管理后台进行配置,如图:

随机算法

加权随机算法负载均衡策略(RandomLoadBalance)是 dubbo 负载均衡的默认实现方式,根据权重分配各个 Invoker 随机选中的比例。这里的意思是:将到达负载均衡流程的 Invoker 列表中的 权重进行求和,然后求出单个 Invoker 权重在总权重中的占比,随机数就在总权重值的范围内生成。

如图,假如当前有 192.168.1.10和 192.168.1.11两个负载均衡的服务,权重分别为 4、6 ,则它们的被选中的比例为 2/5、3/5。

当生成随机数为 6 时,就会选中 192.168.1.11的服务。

dubbo 中 RandomLoadBalance 的 doSelect 实现代码:

public class RandomLoadBalance extends AbstractLoadBalance {public static final String NAME = "random";@Overrideprotected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {// Invoker 数量int length = invokers.size();// 标识所有 Invoker 的权重是否都一样boolean sameWeight = true;// 用一个数组保存每个 Invoker 的权重int[] weights = new int[length];// 第一个 Invoker 的权重int firstWeight = getWeight(invokers.get(0), invocation);weights[0] = firstWeight;// 求和总权重int totalWeight = firstWeight;for (int i = 1; i < length; i++) {int weight = getWeight(invokers.get(i), invocation);// 保存每个 Invoker 的权重到数组总weights[i] = weight;// 累加求和总权重totalWeight += weight;// 如果不是所有 Invoker 的权重都一样,就给标记上 sameWeight = falseif (sameWeight && weight != firstWeight) {sameWeight = false;}}// 计算随机数取到的 Invoker,条件是必须总权重大于0,并且每个 Invoker 的权重都不一样if (totalWeight > 0 && !sameWeight) {// 基于 0~总数 范围内生成随机数int offset = ThreadLocalRandom.current().nextInt(totalWeight);// 计算随机数对应的 Invokerfor (int i = 0; i < length; i++) {offset -= weights[i];if (offset < 0) {return invokers.get(i);}}}// 如果所有 Invoker 的权重都一样则随机从 Invoker 列表中返回一个return invokers.get(ThreadLocalRandom.current().nextInt(length));}
}

以上就是加权随机策略的实现,这里比较主要关注计算生成的随机数对应的 Invoker。通过遍历权重数组,生成的数累减当前权重值,当 offset 为 0 时,就表示 offset 对应当前的 Invoker 服务。

以生成的随机数为 6 为例,遍历 Invokers 长度:

  1. 第一轮:offset = 6 - 4 = 2 不满足 offset < 0,继续遍历。

  2. 第二轮:offset = 2 - 6 = -4 满足 offset < 0,返回当前索引对应的 Invoker。因为 offset 返回负数,表示 offset 落在当前 Invoker 权重的区间里。

加权随机策略并非一定按照比例被选到,理论上调用次数越多,分布的比例越接近权重所占的比例。

最少活跃数算法

最小活跃数负载均衡策略(LeastActiveLoadBalance)是从最小活跃数的 Invoker 中进行选择。什么是活跃数呢?活跃数是一个 Invoker 正在处理的请求的数量,当 Invoker 开始处理请求时,会将活跃数加 1,完成请求处理后,将相应 Invoker 的活跃数减 1。找出最小活跃数后,最后根据权重进行选择最终的 Invoker。如果最后找出的最小活跃数相同,则随机从中选中一个 Invoker。

public class LeastActiveLoadBalance extends AbstractLoadBalance {public static final String NAME = "leastactive";@Overrideprotected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {// Invoker 数量int length = invokers.size();// 所有 Invoker 中的最小活跃值都是 -1int leastActive = -1;// 最小活跃值 Invoker 的数量int leastCount = 0;// 最小活跃值 Invoker 在 Invokers 列表中对应的下标位置int[] leastIndexes = new int[length];// 保存每个 Invoker 的权重int[] weights = new int[length];// 总权重int totalWeight = 0;// 第一个最小活跃数的权重int firstWeight = 0;// 最小活跃数 Invoker 列表的权重是否一样boolean sameWeight = true;// 找出最小活跃数 Invoker 的下标for (int i = 0; i < length; i++) {Invoker<T> invoker = invokers.get(i);// 获取最小活跃数int active = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive();// 获取权重int afterWarmup = getWeight(invoker, invocation);// 保存权重weights[i] = afterWarmup;// 如果当前最小活跃数为-1(-1为最小值)或小于leastActiveif (leastActive == -1 || active < leastActive) {// 重置最小活跃数leastActive = active;// 重置最小活跃数 Invoker 的数量leastCount = 1;// 保存当前 Invoker 在 Invokers 列表中的索引至leastIndexes数组中leastIndexes[0] = i;// 重置最小活跃数 invoker 的总权重值totalWeight = afterWarmup;// 记录当前 Invoker 权重为第一个最小活跃数 Invoker 的权重firstWeight = afterWarmup;// 因为当前 Invoker 重置为第一个最小活跃数 Invoker ,所以标识所有最小活跃数 Invoker 权重都一样的值为 truesameWeight = true;// 如果当前最小活跃数和已声明的最小活跃数相等} else if (active == leastActive) {// 记录当前 Invoker 的位置leastIndexes[leastCount++] = i;// 累加当前 Invoker 权重到总权重中totalWeight += afterWarmup;// 如果当前权重与firstWeight不相等,则将 sameWeight 改为 falseif (sameWeight && i > 0&& afterWarmup != firstWeight) {sameWeight = false;}}}// 如果最小活跃数 Invoker 只有一个,直接返回该 Invokerif (leastCount == 1) {return invokers.get(leastIndexes[0]);}if (!sameWeight && totalWeight > 0) {// 根据权重随机从最小活跃数 Invoker 列表中选择一个int offsetWeight = ThreadLocalRandom.current().nextInt(totalWeight);for (int i = 0; i < leastCount; i++) {int leastIndex = leastIndexes[i];offsetWeight -= weights[leastIndex];if (offsetWeight < 0) {return invokers.get(leastIndex);}}}// 如果所有 Invoker 的权重都一样则随机从 Invoker 列表中返回一个return invokers.get(leastIndexes[ThreadLocalRandom.current().nextInt(leastCount)]);}
}

这段代码的整个逻辑就是,从 Invokers 列表中筛选出最小活跃数的 Invoker,然后类似加权随机算法策略方式选择最终的 Invoker 服务。

轮询算法

加权轮询负载均衡策略(RoundRobinLoadBalance)是基于权重来决定轮询的比例。普通轮询会将请求均匀的分布在每个节点,但不能很好调节不同性能服务器的请求处理,所以加权负载均衡来根据权重在轮询机制中分配相对应的请求比例给每台服务器。

public class RoundRobinLoadBalance extends AbstractLoadBalance {public static final String NAME = "roundrobin";private static final int RECYCLE_PERIOD = 60000;protected static class WeightedRoundRobin {private int weight;private AtomicLong current = new AtomicLong(0);private long lastUpdate;public int getWeight() {return weight;}public void setWeight(int weight) {this.weight = weight;current.set(0);}public long increaseCurrent() {return current.addAndGet(weight);}public void sel(int total) {current.addAndGet(-1 * total);}public long getLastUpdate() {return lastUpdate;}public void setLastUpdate(long lastUpdate) {this.lastUpdate = lastUpdate;}}private ConcurrentMap<String, ConcurrentMap<String, WeightedRoundRobin>> methodWeightMap = new ConcurrentHashMap<String, ConcurrentMap<String, WeightedRoundRobin>>();private AtomicBoolean updateLock = new AtomicBoolean();/*** get invoker addr list cached for specified invocation* <p>* <b>for unit test only</b>** @param invokers* @param invocation* @return*/protected <T> Collection<String> getInvokerAddrList(List<Invoker<T>> invokers, Invocation invocation) {String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();Map<String, WeightedRoundRobin> map = methodWeightMap.get(key);if (map != null) {return map.keySet();}return null;}@Overrideprotected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {// key 为 接口名+方法名String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();// 查看缓存中是否存在相应服务接口的信息,如果没有则新添加一个元素到缓存中ConcurrentMap<String, WeightedRoundRobin> map = methodWeightMap.get(key);if (map == null) {methodWeightMap.putIfAbsent(key, new ConcurrentHashMap<String, WeightedRoundRobin>());map = methodWeightMap.get(key);}// 总权重int totalWeight = 0;long maxCurrent = Long.MIN_VALUE;// 当前时间戳long now = System.currentTimeMillis();// 最大 current 的 InvokerInvoker<T> selectedInvoker = null;// 保存选中的 WeightedRoundRobin 对象WeightedRoundRobin selectedWRR = null;// 遍历 Invokers 列表for (Invoker<T> invoker : invokers) {// 从缓存中获取 WeightedRoundRobin 对象String identifyString = invoker.getUrl().toIdentityString();WeightedRoundRobin weightedRoundRobin = map.get(identifyString);// 获取当前 Invoker 对象int weight = getWeight(invoker, invocation);// 如果当前 Invoker 没有对应的 WeightedRoundRobin 对象,则新增一个if (weightedRoundRobin == null) {weightedRoundRobin = new WeightedRoundRobin();weightedRoundRobin.setWeight(weight);map.putIfAbsent(identifyString, weightedRoundRobin);}// 如果当前 Invoker 权重不等于对应的 WeightedRoundRobin 对象中的权重,则重新设置当前权重到对应的 WeightedRoundRobin 对象中if (weight != weightedRoundRobin.getWeight()) {weightedRoundRobin.setWeight(weight);}// 累加权重到 current 中long cur = weightedRoundRobin.increaseCurrent();// 设置 weightedRoundRobin 对象最后更新时间weightedRoundRobin.setLastUpdate(now);// 最大 current 的 Invoker,并赋值给相应的变量if (cur > maxCurrent) {maxCurrent = cur;selectedInvoker = invoker;selectedWRR = weightedRoundRobin;}// 累加权重到总权重中totalWeight += weight;}// 如果 Invokers 列表中的数量不等于缓存map中的数量if (!updateLock.get() && invokers.size() != map.size()) {if (updateLock.compareAndSet(false, true)) {try {// 拷贝 map 到 newMap 中ConcurrentMap<String, WeightedRoundRobin> newMap = new ConcurrentHashMap<String, WeightedRoundRobin>();newMap.putAll(map);// newMap 转化为 IteratorIterator<Entry<String, WeightedRoundRobin>> it = newMap.entrySet().iterator();// 循环删除超过设定时长没更新的缓存while (it.hasNext()) {Entry<String, WeightedRoundRobin> item = it.next();if (now - item.getValue().getLastUpdate() > RECYCLE_PERIOD) {it.remove();}}// 将当前newMap服务缓存中methodWeightMap.put(key, newMap);} finally {updateLock.set(false);}}}// 如果存在被选中的 Invokerif (selectedInvoker != null) {// 计算 current = current - totalWeightselectedWRR.sel(totalWeight);return selectedInvoker;}// 正常情况这里不会到达return invokers.get(0);}
}

上面选中 Invoker 逻辑为:每个 Invoker 都有一个 current 值,初始值为自身权重。在每个 Invoker 中 current=current+weight。遍历完 Invoker 后,current 最大的那个 Invoker 就是本次选中的 Invoker。选中 Invoker 后,将本次 current 值计算 current=current-totalWeight

以上面 192.168.1.10和 192.168.1.11两个负载均衡的服务,权重分别为 4、6 。基于选中前 current=current+weight、选中后 current=current-totalWeight计算公式得出如下

请求次数 选中前 current 选中后 current 被选中服务
1 [4, 6] [4, -4] 192.168.1.11
2 [8, 2] [-2, 2] 192.168.1.10
3 [2, 8] [2, -2] 192.168.1.11
4 [6, 4] [-4, 4] 192.168.1.10
5 [0, 10] [0, 0] 192.168.1.11

一致性 Hash 算法

一致性 Hash 负载均衡策略(ConsistentHashLoadBalance)是让参数相同的请求分配到同一机器上。把每个服务节点分布在一个环上,请求也分布在环形中。以请求在环上的位置,顺时针寻找换上第一个服务节点。如图所示:

同时,为避免请求散列不均匀,dubbo 中会将每个 Invoker 再虚拟多个节点出来,使得请求调用更加均匀。

一致性 Hash 修改配置如下:

<!-- dubbo 默认只对第一个参数进行 hash 标识,指定hash参数 -->
<dubbo:parameter key="hash.arguments" value="1" />
<!-- 虚拟节点数量 -->
<dubbo:parameter key="hash.nodes" value="200" />

一致性 Hash 实现如下:

public class ConsistentHashLoadBalance extends AbstractLoadBalance {public static final String NAME = "consistenthash";/*** Hash nodes name*/public static final String HASH_NODES = "hash.nodes";/*** Hash arguments name*/public static final String HASH_ARGUMENTS = "hash.arguments";private final ConcurrentMap<String, ConsistentHashSelector<?>> selectors = new ConcurrentHashMap<String, ConsistentHashSelector<?>>();@SuppressWarnings("unchecked")@Overrideprotected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {// 获取请求的方法名String methodName = RpcUtils.getMethodName(invocation);// key = 接口名+方法名String key = invokers.get(0).getUrl().getServiceKey() + "." + methodName;// invokers 的 hashcodeint identityHashCode = System.identityHashCode(invokers);// 查看缓存中是否存在对应 key 的数据,或 Invokers 列表是否有过变动。如果没有,则新添加到缓存中,并且返回负载均衡得出的 InvokerConsistentHashSelector<T> selector = (ConsistentHashSelector<T>) selectors.get(key);if (selector == null || selector.identityHashCode != identityHashCode) {selectors.put(key, new ConsistentHashSelector<T>(invokers, methodName, identityHashCode));selector = (ConsistentHashSelector<T>) selectors.get(key);}return selector.select(invocation);}// ConsistentHashSelector class ...
}

doSelect 中主要实现缓存检查和 Invokers 变动检查,一致性 hash 负载均衡的实现在这个内部类 ConsistentHashSelector 中实现。

private static final class ConsistentHashSelector<T> {// 存储虚拟节点private final TreeMap<Long, Invoker<T>> virtualInvokers;// 节点数private final int replicaNumber;// invoker 列表的 hashcode,用来判断 Invoker 列表是否变化private final int identityHashCode;// 请求中用来作Hash映射的参数的索引private final int[] argumentIndex;ConsistentHashSelector(List<Invoker<T>> invokers, String methodName, int identityHashCode) {this.virtualInvokers = new TreeMap<Long, Invoker<T>>();this.identityHashCode = identityHashCode;URL url = invokers.get(0).getUrl();// 获取节点数this.replicaNumber = url.getMethodParameter(methodName, HASH_NODES, 160);// 获取配置中的 参数索引String[] index = COMMA_SPLIT_PATTERN.split(url.getMethodParameter(methodName, HASH_ARGUMENTS, "0"));argumentIndex = new int[index.length];for (int i = 0; i < index.length; i++) {argumentIndex[i] = Integer.parseInt(index[i]);}for (Invoker<T> invoker : invokers) {// 获取 Invoker 中的地址,包括端口号String address = invoker.getUrl().getAddress();// 创建虚拟节点for (int i = 0; i < replicaNumber / 4; i++) {byte[] digest = md5(address + i);for (int h = 0; h < 4; h++) {long m = hash(digest, h);virtualInvokers.put(m, invoker);}}}}// 找出 Invokerpublic Invoker<T> select(Invocation invocation) {// 将参数转为字符串String key = toKey(invocation.getArguments());// 字符串参数转换为 md5byte[] digest = md5(key);// 根据 md5 找出 Invokerreturn selectForKey(hash(digest, 0));}// 将参数拼接成字符串private String toKey(Object[] args) {StringBuilder buf = new StringBuilder();for (int i : argumentIndex) {if (i >= 0 && i < args.length) {buf.append(args[i]);}}return buf.toString();}// 利用 md5 匹配到对应的 Invokerprivate Invoker<T> selectForKey(long hash) {// 找到第一个大于当前 hash 的 InvokerMap.Entry<Long, Invoker<T>> entry = virtualInvokers.ceilingEntry(hash);if (entry == null) {entry = virtualInvokers.firstEntry();}return entry.getValue();}// hash 运算private long hash(byte[] digest, int number) {return (((long) (digest[3 + number * 4] & 0xFF) << 24)| ((long) (digest[2 + number * 4] & 0xFF) << 16)| ((long) (digest[1 + number * 4] & 0xFF) << 8)| (digest[number * 4] & 0xFF))& 0xFFFFFFFFL;}// md5 运算private byte[] md5(String value) {MessageDigest md5;try {md5 = MessageDigest.getInstance("MD5");} catch (NoSuchAlgorithmException e) {throw new IllegalStateException(e.getMessage(), e);}md5.reset();byte[] bytes = value.getBytes(StandardCharsets.UTF_8);md5.update(bytes);return md5.digest();}
}

一致 hash 实现过程就是先创建好虚拟节点,虚拟节点保存在 TreeMap 中。TreeMap 的 key 为配置的参数先进行 md5 运算,然后将 md5 值进行 hash 运算。TreeMap 的 value 为被选中的 Invoker。

最后请求时,计算参数的 hash 值,去从 TreeMap 中获取 Invoker。

总结

Dubbo 负载均衡的实现,技巧上还是比较优雅,可以多多学习其编码思维。在研究其代码时,需要仔细研究其实现原理,否则比较难懂其思想。

特别推荐一个分享架构+算法的优质内容,还没关注的小伙伴,可以长按关注一下:长按订阅更多精彩▼如有收获,点个在看,诚挚感谢

Dubbo 负载均衡的实现相关推荐

  1. cstring只获取到第一个数_一文讲透 Dubbo 负载均衡之最小活跃数算法

    (给ImportNew加星标,提高Java技能) 作者:why技术(本文来自作者投稿) 本文是对于Dubbo负载均衡策略之一的最小活跃数算法的详细分析.文中所示源码,没有特别标注的地方均为2.6.0版 ...

  2. dubbo调用失败策略_面试官:dubbo负载均衡策略,集群容错策略,动态代理策略有哪些...

    面试官心理分析 继续深问吧,这些都是用 dubbo 必须知道的一些东西,你得知道基本原理,知道序列化是什么协议,还得知道具体用 dubbo 的时候,如何负载均衡,如何高可用,如何动态代理. 说白了,就 ...

  3. dubbo负载均衡策略和集群容错策略

    dubbo负载均衡策略 random loadbalance 默认情况下,dubbo是random load balance随机调用实现负载均衡,可以对provider不同实例设置不同的权重,会按照权 ...

  4. 一文讲透Dubbo负载均衡之最小活跃数算法

    本文是对于Dubbo负载均衡策略之一的最小活跃数算法的详细分析.文中所示源码,没有特别标注的地方均为2.6.0版本. 为什么没有用截止目前的最新的版本号2.7.4.1呢?因为2.6.0这个版本里面有两 ...

  5. Dubbo面试 - dubbo 负载均衡策略和集群容错策略都有哪些?

    Dubbo面试 - dubbo 负载均衡策略和集群容错策略都有哪些? 面试题 dubbo 负载均衡策略和集群容错策略都有哪些?动态代理策略呢? 面试官心理分析 继续深问吧,这些都是用 dubbo 必须 ...

  6. dubbo负载均衡策略配置

    前言 在生产环境中,服务的集群部署是常有的事,从消费端来说,本身并不关注所需要的服务是由哪台机器提供,但是为了应用的健壮性和高可用性,从消费端来说,可以配置一定的负载均衡策略,确保消费端的应用能够及时 ...

  7. dubbo负载均衡代码分析1(leastactive策略)

    2019独角兽企业重金招聘Python工程师标准>>> 接上篇https://my.oschina.net/u/146130/blog/1569554 既然有集群容错,自然会有负载均 ...

  8. Java编程解密-Dubbo负载均衡与集群容错机制

    1 Dubbo简介 Dubbo是一款高性能.轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现. 作为一个轻量级RPC框架,Du ...

  9. Dubbo负载均衡的源码流程(2022.5.30)

    Dubbo负载均衡的源码流程 1.默认负载均衡策略:RandomLoadBalance(随机策略) 2.负载均衡策略存在以下五种: 2.1 RandomLoadBalance(随机) 2.2 Roun ...

最新文章

  1. 【开源方案共享】无序点云快速的线段分割算法
  2. HashMap的31连环炮,我倒在第5个上
  3. Vue中插入HTML代码的方法
  4. mysql 添加索引
  5. 《如何高效学习》读书笔记(一)——整体性学习策略
  6. python class类_python中的class(类)
  7. Jsp中的动态INCLUDE和静态INCLUDE的区别?
  8. OpenCV提供的各种阈值选项的实例(附完整代码)
  9. windows删除桌面ie_从Windows 8“开始”屏幕启动IE的桌面版本
  10. eth一张_听说eth2.0利好落地了?那么为何eth反而涨了?
  11. 如何用stata画莫兰散点图_吃下这颗安利:STATA
  12. Vmware fusion强制断电,关闭虚拟机
  13. 杭电1879继续畅通工程
  14. JavaWeb框架-Spring(基本概念)
  15. 最浅显的IE反劫持攻略(转)
  16. QT相关内容的下载链接
  17. 微信小程序map 地图引入配置+腾讯地图地址坐标解析
  18. Linux手机众筹1000万
  19. CBTC系统标准: 1474.2---系统用户接口需求
  20. 怎么给文件夹加密码 电脑文件夹加密方法

热门文章

  1. P4216 [SCOI2015]情报传递 LCA+树上主席树 离线操作
  2. java 注解开发_Java中的注解到底是如何工作的?
  3. 模板 - DP动态规划
  4. 二叉树(构造与遍历方法)
  5. python字节转字符串中文乱码_黄聪:解决python中文处理乱码,先要弄懂“字符”和“字节”的差别...
  6. oracle bulk collect
  7. 语音合成android代码,百度 TTS 语音合成-Android
  8. multisim变压器反馈式_变压器的分类及特点
  9. Netty序章之BIO NIO AIO演变
  10. 人算不如“云算”,且看新时代“借东风”