Dubbo的几个负载均衡策略-轮询

-----------------看过之前一致性哈希的可以跳过----------------- 

链接在此:Dubbo的几个负载均衡类--一致性哈希

Dubbo的几个负载均衡类--最少活跃数

Dubbo的几个负载均衡类--随机

消费者发起调用过程中涉及如下几步

1:接口调用,比如DemoService.demoMethod

2:InvokerInvocationHandler.invoker:消费端启动时,通过JavassistProxyFactory.getProxy反射获取代理类,之后服务调用就直接调用这个Handler

3:MigrationInvoker.invoke:Dubbo 发起调用非常重要的一步,如果失败了,通过这个invoker做切换

4:其他

5:FailoverClusterInvoker.invoke(目前我们使用的,实际在AbstractClusterInvoker里面invoke逻辑是固定的)

6:其他

@Override
    public Result invoke(final Invocation invocation) throws RpcException {
        checkWhetherDestroyed();

        // binding attachments into invocation.
        Map contextAttachments = RpcContext.getContext().getObjectAttachments();
        if (contextAttachments != null && contextAttachments.size() != 0) {
            ((RpcInvocation) invocation).addObjectAttachments(contextAttachments);
        }
        //如果设置了标签规则,则通过list方法过滤出来符合标签的几个invoker
        List> invokers = list(invocation);
        LoadBalance loadbalance = initLoadBalance(invokers, invocation);
        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
        //请求负载并且做好灾备降级
        return doInvoke(invocation, invokers, loadbalance);
    }

public Result doInvoke(Invocation invocation, final List> invokers, LoadBalance loadbalance) throws RpcException {
        List> copyInvokers = invokers;
        checkInvokers(copyInvokers, invocation);
        String methodName = RpcUtils.getMethodName(invocation);
        int len = calculateInvokeTimes(methodName);
        // retry loop.
        RpcException le = null; // last exception.
        List> invoked = new ArrayList>(copyInvokers.size()); // invoked invokers.
        Set providers = new HashSet(len);
        for (int i = 0; i < len; i++) {
            //Reselect before retry to avoid a change of candidate `invokers`.
            //NOTE: if `invokers` changed, then `invoked` also lose accuracy.
            if (i > 0) {
                checkWhetherDestroyed();
                copyInvokers = list(invocation);
                // check again
                checkInvokers(copyInvokers, invocation);
            }
            //这里通过loadBalance做负载
            Invoker invoker = select(loadbalance, invocation, copyInvokers, invoked);
            invoked.add(invoker);
            RpcContext.getContext().setInvokers((List) invoked);
            try {
                Result result = invoker.invoke(invocation);
                if (le != null && logger.isWarnEnabled()) {
                    logger.warn("Although retry the method " + methodName
                            + " in the service " + getInterface().getName()
                            + " was successful by the provider " + invoker.getUrl().getAddress()
                            + ", but there have been failed providers " + providers
                            + " (" + providers.size() + "/" + copyInvokers.size()
                            + ") from the registry " + directory.getUrl().getAddress()
                            + " on the consumer " + NetUtils.getLocalHost()
                            + " using the dubbo version " + Version.getVersion() + ". Last error is: "
                            + le.getMessage(), le);
                }
                return result;
            } catch (RpcException e) {
                if (e.isBiz()) { // biz exception.
                    throw e;
                }
                le = e;
            } catch (Throwable e) {
                le = new RpcException(e.getMessage(), e);
            } finally {
                providers.add(invoker.getUrl().getAddress());
            }
        }
        throw new RpcException(le.getCode(), "Failed to invoke the method "
                + methodName + " in the service " + getInterface().getName()
                + ". Tried " + len + " times of the providers " + providers
                + " (" + providers.size() + "/" + copyInvokers.size()
                + ") from the registry " + directory.getUrl().getAddress()
                + " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version "
                + Version.getVersion() + ". Last error is: "
                + le.getMessage(), le.getCause() != null ? le.getCause() : le);
    }

-----------------看过之前一致性哈希的可以跳过----------------- 

轮询的代码不算多,代码如下,下面将会一点一点的说明

@Override
    protected  Invoker doSelect(List> invokers, URL url, Invocation invocation) {
        String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
        ConcurrentMap map = methodWeightMap.computeIfAbsent(key, k -> new ConcurrentHashMap<>());
        int totalWeight = 0;
        long maxCurrent = Long.MIN_VALUE;
        long now = System.currentTimeMillis();
        Invoker selectedInvoker = null;
        WeightedRoundRobin selectedWRR = null;
        for (Invoker invoker : invokers) {
            String identifyString = invoker.getUrl().toIdentityString();
            int weight = getWeight(invoker, invocation);
            WeightedRoundRobin weightedRoundRobin = map.computeIfAbsent(identifyString, k -> {
                WeightedRoundRobin wrr = new WeightedRoundRobin();
                wrr.setWeight(weight);
                return wrr;
            });

            if (weight != weightedRoundRobin.getWeight()) {
                //weight changed
                weightedRoundRobin.setWeight(weight);
            }
            long cur = weightedRoundRobin.increaseCurrent();
            weightedRoundRobin.setLastUpdate(now);
            if (cur > maxCurrent) {
                maxCurrent = cur;
                selectedInvoker = invoker;
                selectedWRR = weightedRoundRobin;
            }
            totalWeight += weight;
        }
        if (invokers.size() != map.size()) {
            map.entrySet().removeIf(item -> now - item.getValue().getLastUpdate() > RECYCLE_PERIOD);
        }
        if (selectedInvoker != null) {
            selectedWRR.sel(totalWeight);
            return selectedInvoker;
        }
        // should not happen here
        return invokers.get(0);
    }

方法getWeight是处理那些启动在10分钟内的服务,做一定的服务负载降级。超过十分钟,那么返回的负载都是100,如果小于1分钟那么就是1,1分钟到10分钟之间分别的是分钟数的平方值。

基本针对的就是我们在版本发布的时候,需要启动某个服务的所有实例,这时候越早启动的权重值越大,这么做的原因其实也很合理,越早启动的越稳定,包括各种缓存热点的加载,连接数/链接池的初始化等等

 轮询好像也不是我之前想象的轮询,而是每次把当前最大权重的invoker算出来,然后当前调用的就是它了,为了下一次不在轮询到它,就把当前的invoker的权重减去总权重

if (selectedInvoker != null) {
            selectedWRR.sel(totalWeight);
            return selectedInvoker;
        }

 public void sel(int total) {
            current.addAndGet(-1 * total);
        }

总结:轮询算法好像无蟹可鸡哦

你可能感兴趣的:(Dubbo,dubbo,负载均衡)