<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Dubbo:負(fù)載均衡實(shí)現(xiàn)解析

          共 22300字,需瀏覽 45分鐘

           ·

          2022-07-23 15:29

          走過(guò)路過(guò)不要絕緣

          點(diǎn)擊藍(lán)字關(guān)注我們


          dubbo作為分布式遠(yuǎn)程調(diào)用框架,要保證的點(diǎn)很多,比如:服務(wù)注冊(cè)與發(fā)現(xiàn)、故障轉(zhuǎn)移、高性能通信、負(fù)載均衡等等!


          負(fù)載均衡的目的是為了特定場(chǎng)景下,能夠?qū)⒄?qǐng)求合理地平分到各服務(wù)實(shí)例上,以便發(fā)揮所有機(jī)器的疊加作用。主要考慮的點(diǎn)如:不要分配請(qǐng)求到掛掉的機(jī)器,性能越好的機(jī)器可以分配更多的請(qǐng)求。。。


          一般負(fù)載均衡是借助外部工具,硬件負(fù)載均衡或軟件負(fù)載均衡,如F5/nginx。當(dāng)然了,在當(dāng)前分布式環(huán)境遍地開花的情況下,客戶端的負(fù)載均衡看起來(lái)就更輕量級(jí),顯得不可或缺。


          今天我們就來(lái)看看dubbo是如何進(jìn)行負(fù)載均衡的吧!


          1. dubbo負(fù)載均衡的作用?


          其出發(fā)點(diǎn),自然也就是普通的負(fù)載均衡器的出發(fā)點(diǎn)了。將負(fù)載均衡功能實(shí)現(xiàn)在rpc客戶端側(cè),以便能夠隨時(shí)適應(yīng)外部的環(huán)境變化,更好地發(fā)揮硬件作用。而且客戶端的負(fù)載均衡

          天然地就避免了單點(diǎn)問(wèn)題。定制化的自有定制化的優(yōu)勢(shì)和劣勢(shì)。


          它可以從配置文件中指定,也可以在管理后臺(tái)進(jìn)行配置修改。


          事實(shí)上,它支持 服務(wù)端服務(wù)/方法級(jí)別、客戶端服務(wù)/方法級(jí)別 的負(fù)載均衡配置。



          2. dubbo有哪些負(fù)載均衡方式?


          即dubbo提供了哪些負(fù)載均衡策略呢?


          Dubbo內(nèi)置了4種負(fù)載均衡策略:


          RandomLoadBalance:隨機(jī)負(fù)載均衡。隨機(jī)的選擇一個(gè)。是Dubbo的默認(rèn)負(fù)載均衡策略。

          RoundRobinLoadBalance:輪詢負(fù)載均衡。輪詢選擇一個(gè)。


          LeastActiveLoadBalance:最少活躍調(diào)用數(shù),相同活躍數(shù)的隨機(jī)。活躍數(shù)指調(diào)用前后計(jì)數(shù)差。使慢的 Provider 收到更少請(qǐng)求,因?yàn)樵铰?Provider 的調(diào)用前后計(jì)數(shù)差會(huì)越大。


          ConsistentHashLoadBalance:一致性哈希負(fù)載均衡。相同參數(shù)的請(qǐng)求總是落在同一臺(tái)機(jī)器上。


          3. 如何配置dubbo負(fù)載均衡策略?


          其實(shí)在第一點(diǎn)時(shí)已經(jīng)提過(guò),有多種級(jí)別的配置:服務(wù)端服務(wù)/方法級(jí)別、客戶端服務(wù)/方法級(jí)別; 具體配置如下:

          <!-- 服務(wù)端服務(wù)級(jí)別 -->    <dubbo:service interface="..." loadbalance="roundrobin" />    <!-- 客戶端服務(wù)級(jí)別 -->    <dubbo:reference interface="..." loadbalance="roundrobin" />    <!-- 服務(wù)端方法級(jí)別 -->    <dubbo:service interface="...">        <dubbo:method name="hello" loadbalance="roundrobin"/>    </dubbo:service>    <!-- 客戶端方法級(jí)別 -->    <dubbo:reference interface="...">        <dubbo:method name="hello" loadbalance="roundrobin"/>    </dubbo:reference>

          多個(gè)配置是有覆蓋關(guān)系的, 配置的優(yōu)先級(jí)是:

          1. 客戶端方法級(jí)別配置;(最優(yōu)先)
          2. 客戶端接口級(jí)別配置;
          3. 服務(wù)端方法級(jí)別配置;
          4. 服務(wù)端接口級(jí)別配置;(最后使用)

          注意: 雖說(shuō)以上配置有全封閉服務(wù)端配置的,有針對(duì)客戶端配置的,但是,真正使負(fù)載均衡起作用的是,客戶端在發(fā)起調(diào)用的時(shí)候,使用相應(yīng)負(fù)載均衡算法進(jìn)行選擇調(diào)用。(服務(wù)端不可能有這能力)

          負(fù)載均衡器的初始化過(guò)程如下:

          // 調(diào)用提供者服務(wù)入口    // org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker#invoke    @Override    public Result invoke(final Invocation invocation) throws RpcException {        checkWhetherDestroyed();
          // binding attachments into invocation. Map<String, Object> contextAttachments = RpcContext.getContext().getAttachments(); if (contextAttachments != null && contextAttachments.size() != 0) { ((RpcInvocation) invocation).addAttachments(contextAttachments); }
          List<Invoker<T>> invokers = list(invocation); // 根據(jù)可用的提供者列表和要調(diào)用的方法,決定選取的負(fù)載均衡器 LoadBalance loadbalance = initLoadBalance(invokers, invocation); RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation); return doInvoke(invocation, invokers, loadbalance); }
          // 實(shí)例化一個(gè)負(fù)載均衡器,以備后續(xù)使用 // org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker#initLoadBalance /** * Init LoadBalance. * <p> * if invokers is not empty, init from the first invoke's url and invocation * if invokes is empty, init a default LoadBalance(RandomLoadBalance) * </p> * * @param invokers invokers * @param invocation invocation * @return LoadBalance instance. if not need init, return null. */ protected LoadBalance initLoadBalance(List<Invoker<T>> invokers, Invocation invocation) { if (CollectionUtils.isNotEmpty(invokers)) { // 從provider 的 url 地址中取出 loadbalance=xxx 配置,如果沒(méi)有仍使用 random 策略 return ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl() .getMethodParameter(RpcUtils.getMethodName(invocation), LOADBALANCE_KEY, DEFAULT_LOADBALANCE)); } else { // 默認(rèn)是 random 策略 return ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(DEFAULT_LOADBALANCE); } }

          所以,事實(shí)上,到最終客戶端決定使用哪個(gè)負(fù)載均衡策略時(shí),只是從請(qǐng)求參數(shù)中取出 loadbalance=xxx 的參數(shù),進(jìn)而決定具體實(shí)例。前面所有的配置,也都是為決定這個(gè)參數(shù)做出的努力。

          4. dubbo負(fù)載均衡的實(shí)現(xiàn)?

          前面我們看到,dubbo中提供了4種負(fù)載均衡策略,功能也是很明了。那么他們都是如何實(shí)現(xiàn)的呢?

          先來(lái)看下其繼承圖:

           

          很明顯,多個(gè)負(fù)載均衡器都有一些共同點(diǎn),所以統(tǒng)一使用 AbstractLoadBalance 進(jìn)行抽象模板方法,差異點(diǎn)由各子算法決定即可。

          那么抽象類中,到底有多少公用功能被抽取出來(lái)了呢?到底什么是公用的呢?

          // org.apache.dubbo.rpc.cluster.loadbalance.AbstractLoadBalance#select    @Override    public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {        // 沒(méi)有可用的提供者,沒(méi)得選        if (CollectionUtils.isEmpty(invokers)) {            return null;        }        // 只有一個(gè)提供者,沒(méi)得均衡的,直接用        if (invokers.size() == 1) {            return invokers.get(0);        }        // 然后就是各自均衡算法的實(shí)現(xiàn)了        return doSelect(invokers, url, invocation);    }

          好吧,看起來(lái)是我想多了。抽象方法并沒(méi)有太多的職責(zé),僅做普通判空操作而已。不過(guò)它倒是提供幾個(gè)公用方法被調(diào)用,如 getWeight();

          事實(shí)上,模板方法更多地存在于集群的抽象調(diào)用方法中。AbstractClusterInvoker 。

          整個(gè)負(fù)載均衡的功能,都被統(tǒng)一放在 cluster 模塊下的 loadbalance 包下,一看即明了。

           

          還是來(lái)看具體的實(shí)現(xiàn)好玩些!


          4.1. 隨機(jī)負(fù)載均衡的實(shí)現(xiàn)

          /** * This class select one provider from multiple providers randomly. * You can define weights for each provider: * If the weights are all the same then it will use random.nextInt(number of invokers). * If the weights are different then it will use random.nextInt(w1 + w2 + ... + wn) * Note that if the performance of the machine is better than others, you can set a larger weight. * If the performance is not so good, you can set a smaller weight. */public class RandomLoadBalance extends AbstractLoadBalance {    // 標(biāo)識(shí)自身    public static final String NAME = "random";
          /** * 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; // 計(jì)算出所有權(quán)重和,以便在進(jìn)行隨機(jī)時(shí)設(shè)定范圍 totalWeight += weight; if (sameWeight && weight != firstWeight) { sameWeight = false; } } // 針對(duì)各提供供者權(quán)重不一的情況,則找到第一個(gè)大于隨機(jī)數(shù)的提供者即可 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. // 如果大家權(quán)重都一樣,則直接以個(gè)數(shù)進(jìn)行隨機(jī)即可得到提供者 return invokers.get(ThreadLocalRandom.current().nextInt(length)); }
          }

          稍微有點(diǎn)小技巧的就是,針對(duì)不一樣權(quán)重的隨機(jī)實(shí)現(xiàn),以相減的方式找到第一個(gè)為負(fù)的提供者即可。注意,此處計(jì)算各提供者權(quán)重的算法,倒成了難點(diǎn)了有木有。


          4.2. 輪詢負(fù)載均衡的實(shí)現(xiàn)

          /** * Round robin load balance. */public class RoundRobinLoadBalance extends AbstractLoadBalance {    // 自身標(biāo)識(shí)    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; }
          @Override protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) { 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(); Invoker<T> selectedInvoker = null; WeightedRoundRobin selectedWRR = null; for (Invoker<T> invoker : invokers) { String identifyString = invoker.getUrl().toIdentityString(); WeightedRoundRobin weightedRoundRobin = map.get(identifyString); int weight = getWeight(invoker, invocation);
          if (weightedRoundRobin == null) { weightedRoundRobin = new WeightedRoundRobin(); weightedRoundRobin.setWeight(weight); map.putIfAbsent(identifyString, weightedRoundRobin); } if (weight != weightedRoundRobin.getWeight()) { //weight changed weightedRoundRobin.setWeight(weight); } // 自增權(quán)重 long cur = weightedRoundRobin.increaseCurrent(); weightedRoundRobin.setLastUpdate(now); // 獲取最大權(quán)重項(xiàng),并以對(duì)應(yīng)的 invoker 作為本次選擇的實(shí)例 if (cur > maxCurrent) { maxCurrent = cur; selectedInvoker = invoker; selectedWRR = weightedRoundRobin; } totalWeight += weight; } // 當(dāng)invoker數(shù)量發(fā)生變化時(shí),需要能感知到,以便清理 map, 避免內(nèi)存泄露 if (!updateLock.get() && invokers.size() != map.size()) { if (updateLock.compareAndSet(false, true)) { try { // copy -> modify -> update reference // 超出計(jì)數(shù)周期,則清空原來(lái)的 WeightedRoundRobin ConcurrentMap<String, WeightedRoundRobin> newMap = new ConcurrentHashMap<>(map); newMap.entrySet().removeIf(item -> now - item.getValue().getLastUpdate() > RECYCLE_PERIOD); methodWeightMap.put(key, newMap); } finally { updateLock.set(false); } } } if (selectedInvoker != null) { // 將本次選中的invoker, 權(quán)重置為最低, 以便下次不會(huì)再被選中 selectedWRR.sel(totalWeight); return selectedInvoker; } // should not happen here return invokers.get(0); }
          }

          依次從最大權(quán)重的invoker開始選擇,然后將選中的項(xiàng)放到最后,輪流選中。使用一個(gè) ConcurrentHashMap 來(lái)保存每個(gè)url的權(quán)重信息,且維護(hù)其活躍性。

          4.3. 最少活躍調(diào)用數(shù)負(fù)載均衡的實(shí)現(xiàn)

          /** * LeastActiveLoadBalance * <p> * Filter the number of invokers with the least number of active calls and count the weights and quantities of these invokers. * If there is only one invoker, use the invoker directly; * if there are multiple invokers and the weights are not the same, then random according to the total weight; * if there are multiple invokers and the same weight, then randomly called. */public class LeastActiveLoadBalance extends AbstractLoadBalance {    // 自身標(biāo)識(shí)    public static final String NAME = "leastactive";
          @Override protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) { // Number of invokers int length = invokers.size(); // The least active value of all invokers int leastActive = -1; // The number of invokers having the same least active value (leastActive) int leastCount = 0; // The index of invokers having the same least active value (leastActive) int[] leastIndexes = new int[length]; // the weight of every invokers int[] weights = new int[length]; // The sum of the warmup weights of all the least active invokers int totalWeight = 0; // The weight of the first least active invoker int firstWeight = 0; // Every least active invoker has the same weight value? boolean sameWeight = true;

          // Filter out all the least active invokers for (int i = 0; i < length; i++) { Invoker<T> invoker = invokers.get(i); // Get the active number of the invoker int active = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive(); // Get the weight of the invoker's configuration. The default value is 100. int afterWarmup = getWeight(invoker, invocation); // save for later use weights[i] = afterWarmup; // If it is the first invoker or the active number of the invoker is less than the current least active number if (leastActive == -1 || active < leastActive) { // Reset the active number of the current invoker to the least active number leastActive = active; // Reset the number of least active invokers leastCount = 1; // Put the first least active invoker first in leastIndexes leastIndexes[0] = i; // Reset totalWeight totalWeight = afterWarmup; // Record the weight the first least active invoker firstWeight = afterWarmup; // Each invoke has the same weight (only one invoker here) sameWeight = true; // If current invoker's active value equals with leaseActive, then accumulating. } else if (active == leastActive) { // Record the index of the least active invoker in leastIndexes order leastIndexes[leastCount++] = i; // Accumulate the total weight of the least active invoker totalWeight += afterWarmup; // If every invoker has the same weight? if (sameWeight && i > 0 && afterWarmup != firstWeight) { sameWeight = false; } } } // Choose an invoker from all the least active invokers if (leastCount == 1) { // 如果只有一個(gè)最小則直接返回 // If we got exactly one invoker having the least active value, return this invoker directly. return invokers.get(leastIndexes[0]); } if (!sameWeight && totalWeight > 0) { // If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on // totalWeight. // 如果權(quán)重不相同且權(quán)重大于0則按總權(quán)重?cái)?shù)隨機(jī) // 并確定隨機(jī)值落在哪個(gè)片斷上(第一個(gè)相減為負(fù)的值) int offsetWeight = ThreadLocalRandom.current().nextInt(totalWeight); // Return a invoker based on the random value. for (int i = 0; i < leastCount; i++) { int leastIndex = leastIndexes[i]; offsetWeight -= weights[leastIndex]; if (offsetWeight < 0) { return invokers.get(leastIndex); } } } // If all invokers have the same weight value or totalWeight=0, return evenly. return invokers.get(leastIndexes[ThreadLocalRandom.current().nextInt(leastCount)]); }}

          官方解釋:最少活躍調(diào)用數(shù),相同活躍數(shù)的隨機(jī),活躍數(shù)指調(diào)用前后計(jì)數(shù)差,使慢的機(jī)器收到更少。

          額,有點(diǎn)難以理解的樣子。

          4.4. 一致性哈希負(fù)載均衡的實(shí)現(xiàn)

          /** * ConsistentHashLoadBalance */public class ConsistentHashLoadBalance extends AbstractLoadBalance {    public static final String NAME = "consistenthash";
          /** * Hash nodes name * 通過(guò) 指定 hash.nodes=0,1,2... 可以自定義參與一致性hash的參數(shù)列表 */ public static final String HASH_NODES = "hash.nodes";
          /** * Hash arguments name */ public static final String HASH_ARGUMENTS = "hash.arguments"; // 使用selector 保存某個(gè)固定狀態(tài)時(shí) invoker 的映射關(guān)系 private final ConcurrentMap<String, ConsistentHashSelector<?>> selectors = new ConcurrentHashMap<String, ConsistentHashSelector<?>>();
          @SuppressWarnings("unchecked") @Override protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) { String methodName = RpcUtils.getMethodName(invocation); String key = invokers.get(0).getUrl().getServiceKey() + "." + methodName; int identityHashCode = System.identityHashCode(invokers); ConsistentHashSelector<T> selector = (ConsistentHashSelector<T>) selectors.get(key); // 第一次進(jìn)入或者 identityHashCode 不相等時(shí)(invoker環(huán)境發(fā)生了變化) 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); }
          private static final class ConsistentHashSelector<T> {
          private final TreeMap<Long, Invoker<T>> virtualInvokers;
          private final int replicaNumber;
          private final int identityHashCode;
          private final int[] argumentIndex;
          ConsistentHashSelector(List<Invoker<T>> invokers, String methodName, int identityHashCode) { // 存放虛擬節(jié)點(diǎn) this.virtualInvokers = new TreeMap<Long, Invoker<T>>(); this.identityHashCode = identityHashCode; URL url = invokers.get(0).getUrl(); // hash.nodes 默認(rèn)是 160 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) { 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); } } } }
          public Invoker<T> select(Invocation invocation) { // 取出參與一致性hash計(jì)算的參數(shù)信息 String key = toKey(invocation.getArguments()); byte[] digest = md5(key); // 根據(jù)hash值選取 invoker return selectForKey(hash(digest, 0)); }
          private String toKey(Object[] args) { StringBuilder buf = new StringBuilder(); // 按照指定的參與hash的參數(shù),調(diào)用 toString() 方法,得到參數(shù)標(biāo)識(shí)信息 for (int i : argumentIndex) { if (i >= 0 && i < args.length) { buf.append(args[i]); } } return buf.toString(); }
          private Invoker<T> selectForKey(long hash) { // ceilingEntry Map.Entry<Long, Invoker<T>> entry = virtualInvokers.ceilingEntry(hash); // 如果沒(méi)有找到,取第一個(gè)值 if (entry == null) { entry = virtualInvokers.firstEntry(); } return entry.getValue(); }
          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; }
          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(); }
          }
          }


          主要就是取第多少個(gè)參數(shù),參與hashCode的計(jì)算,然后按照一致性hash算法,定位invoker. 它使用一個(gè) TreeMap 來(lái)保存一致性哈希虛擬節(jié)點(diǎn),hashCode->invoker形式存儲(chǔ),使用 ceilingEntry(hash) 的方式獲取最近的虛擬節(jié)點(diǎn)(天然的一致性hash應(yīng)用)。

          值得一提的是,一致性哈希負(fù)載均衡策略是唯一一個(gè)沒(méi)有使用到權(quán)重項(xiàng)的負(fù)載均衡算法。而前面幾種均衡算法,多少都與權(quán)重相關(guān)。該負(fù)載均衡的應(yīng)用場(chǎng)景嘛,還得自己找了。


          5. 更多的負(fù)載均衡策略?

          dubbo實(shí)現(xiàn)了4種負(fù)載均衡策略,是否就只能是這么多呢?一個(gè)好的設(shè)計(jì)是不會(huì)的,對(duì)擴(kuò)展開放?;赿ubbo的SPI機(jī)制,可以自行實(shí)現(xiàn)任意的負(fù)載均衡策略!

          1. 實(shí)現(xiàn) LoadBalance 接口;
          2. 添加資源文件 添加文件:src/main/resource/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance;

          demo=my=com.demo.dubbo.DemoLoadBalance

          3. 設(shè)置負(fù)載均衡策略為自己實(shí)現(xiàn)的demo;


          6. 有了負(fù)載均衡就可以保證高可用了嗎?


          當(dāng)然不能。負(fù)載均衡只是保證了在發(fā)生調(diào)用的時(shí)候,可以將流量按照既定規(guī)定均攤到各機(jī)器上。然而,均攤是不是最合理的方式卻是不一定的。另外,如果發(fā)生異常,此次負(fù)載均衡就失敗了,從而成功躲過(guò)了高可用。

          事實(shí)上,dubbo用三種方式協(xié)同保證了高可用:

          1. 負(fù)載均衡
          2. 集群容錯(cuò)
          3. 服務(wù)路由

          以下故事描述摘自官網(wǎng):

          這3個(gè)概念容易混淆。他們都描述了怎么從多個(gè) Provider 中選擇一個(gè)來(lái)進(jìn)行調(diào)用。那他們到底有什么區(qū)別呢?下面我來(lái)舉一個(gè)簡(jiǎn)單的例子,把這幾個(gè)概念闡述清楚吧。
          有一個(gè)Dubbo的用戶服務(wù),在北京部署了10個(gè),在上海部署了20個(gè)。一個(gè)杭州的服務(wù)消費(fèi)方發(fā)起了一次調(diào)用,然后發(fā)生了以下的事情:
          根據(jù)配置的路由規(guī)則,如果杭州發(fā)起的調(diào)用,會(huì)路由到比較近的上海的20個(gè) Provider。
          根據(jù)配置的隨機(jī)負(fù)載均衡策略,在20個(gè) Provider 中隨機(jī)選擇了一個(gè)來(lái)調(diào)用,假設(shè)隨機(jī)到了第7個(gè) Provider。
          結(jié)果調(diào)用第7個(gè) Provider 失敗了。
          根據(jù)配置的Failover集群容錯(cuò)模式,重試其他服務(wù)器。
          重試了第13個(gè) Provider,調(diào)用成功。
          上面的第1,2,4步驟就分別對(duì)應(yīng)了路由,負(fù)載均衡和集群容錯(cuò)。Dubbo中,先通過(guò)路由,從多個(gè) Provider 中按照路由規(guī)則,選出一個(gè)子集。再根據(jù)負(fù)載均衡從子集中選出一個(gè) Provider 進(jìn)行本次調(diào)用。如果調(diào)用失敗了,根據(jù)集群容錯(cuò)策略,進(jìn)行重試或定時(shí)重發(fā)或快速失敗等??梢钥吹紻ubbo中的路由,負(fù)載均衡和集群容錯(cuò)發(fā)生在一次RPC調(diào)用的不同階段。最先是路由,然后是負(fù)載均衡,最后是集群容錯(cuò)。 






          往期精彩推薦



          騰訊、阿里、滴答后臺(tái)面試題匯總——(含回答)

          歷史最全面試面試題!

          最新阿里內(nèi)推Java話題

          JVM難學(xué)那是因?yàn)槟銢](méi)認(rèn)真閱讀這篇文章?


          結(jié)束


          關(guān)注作者微信公眾號(hào) — 《JAVA爛豬皮》


          了解更多java典型知識(shí)以及最新面試寶


          你點(diǎn)的每一個(gè)樣子,我都認(rèn)真當(dāng)成了


          看這篇記得給作者點(diǎn)贊+在看哦~~~大家的支持,是源源不斷出文的動(dòng)力

          瀏覽 59
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产黄色A级毛片 | 男女激情视频在线观看 | 欧美精品高清无码 | 免费观看国产一卡二卡电影 | 韩国二区18 |