Source depth Dubbo - Dubbo consumers calling process

Disclaimer: This article is a blogger original article, follow the CC 4.0 BY-SA copyright agreement, reproduced, please attach the original source link and this statement.
This link: https://blog.csdn.net/red_sheeps/article/details/85649067

Sample code for this article see here .
Also, you can view other Repository, also may be concerned about the public micro-channel number, depth source for a good weekly article, please share on learning -


Before've learned, Dubbo how to load the configuration file, how to initialize Bean the. It was just Dubbo ready to run real work, not design Dubbo the core, I also not very understanding, just to interview and prepare those generalities. Now consider a step by step learning recording of the Dubbo call process, add a personal understanding of the critical code, we want to help.

The calling process

Invoke the proxy class

    // 该类实现InvocationHandler接口,在调用动态代理类方法时会调用invoke方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        Class<?>[] parameterTypes = method.getParameterTypes();
        if (method.getDeclaringClass() == Object.class) {
            return method.invoke(invoker, args);
        }
        if ("toString".equals(methodName) && parameterTypes.length == 0) {
            return invoker.toString();
        }
        if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
            return invoker.hashCode();
        }
        if ("equals".equals(methodName) && parameterTypes.length == 1) {
            return invoker.equals(args[0]);
        }
        // 如果是调用基础方法,直接调用invoker的方法并返回。调用其他方法,则将本次调用信息封装成RpcInvocation对象
        // (详见下文)
        return invoker.invoke(new RpcInvocation(method, args)).recreate();
    }

Note:

  • Want to understand the dynamic proxy demo can be viewed here .

MockClusterInvoker invoke execution method

    @Override
    public Result invoke(Invocation invocation) throws RpcException {
        Result result = null;

        // 从directory中获取调用的方法信息中存储的配置信息Constants.MOCK_KEY(是否使用mock模式,默认为false)
        String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim();
        // 如果为false,则调用真实服务提供者
        if (value.length() == 0 || value.equalsIgnoreCase("false")) {
            //no mock
            result = this.invoker.invoke(invocation);
        } else if (value.startsWith("force")) {// 如果该配置以force开头,则进行强制mock调用,执行doMockInvoke方法(具体可查看方法内实现,该内容不是本文重点)
            if (logger.isWarnEnabled()) {
                logger.info("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + directory.getUrl());
            }
            //force:direct mock
            result = doMockInvoke(invocation, null);
        } else {// 其他配置,则优先调用服务提供者,如果失败再进行mock
            //fail-mock
            try {
                // (详见下文)
                result = this.invoker.invoke(invocation);
            } catch (RpcException e) {
                if (e.isBiz()) {
                    throw e;
                } else {
                    if (logger.isWarnEnabled()) {
                        logger.warn("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + directory.getUrl(), e);
                    }
                    // 会catch RpcException异常,失败后执行doMockInvoke方法
                    result = doMockInvoke(invocation, e);
                }
            }
        }
        return result;
    }

AbstractClusterInvoker invoke execution method

    @Override
    public Result invoke(final Invocation invocation) throws RpcException {
        checkWhetherDestroyed();
        LoadBalance loadbalance = null;

        // 查看线程变量InternalThreadLocal中获取上下文对象RpcContext,查看RpcContext中是否有"附件"属性,如果有,则本次调用invocation加入该属性
        // binding attachments into invocation.
        Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
        if (contextAttachments != null && contextAttachments.size() != 0) {
            ((RpcInvocation) invocation).addAttachments(contextAttachments);
        }
        
        // 从directory中获取invokers list(查看directory对象,该list可通过notify方法与注册中心交互更新)
        List<Invoker<T>> invokers = list(invocation);
        if (invokers != null && !invokers.isEmpty()) {
            // 获取负载均衡策略,未配置则默认随机策略RandomLoadBalance
            loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
                    .getMethodParameter(RpcUtils.getMethodName(invocation), Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE));
        }
        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
        // 执行子类doInvoke方法(详见下文)
        return doInvoke(invocation, invokers, loadbalance);
    }

AbstractClusterInvoker subclass FailoverClusterInvoker method performed doInvoke

public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
        List<Invoker<T>> copyinvokers = invokers;
        // 检查invokers,为空则抛出异常RpcException
        checkInvokers(copyinvokers, invocation);
        // 获取重试次数,默认是2次
        int len = getUrl().getMethodParameter(invocation.getMethodName(), Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES) + 1;
        // 如果配置不当,则将次数置为1
        if (len <= 0) {
            len = 1;
        }
        // retry loop.   循环调用
        RpcException le = null; // last exception.
        List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyinvokers.size()); // invoked invokers.
        Set<String> providers = new HashSet<String>(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   再一次检查,防止在调用过程中list中的invoker变化
                checkInvokers(copyinvokers, invocation);
            }
            // 通过不同的负载均衡策略选择一个invoker
            Invoker<T> invoker = select(loadbalance, invocation, copyinvokers, invoked);
            // 将已经选择出的invoker存放在另一个list中
            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 " + invocation.getMethodName()
                            + " 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 != null ? le.getCode() : 0, "Failed to invoke the method "
                + invocation.getMethodName() + " 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 != null ? le.getMessage() : ""), le != null && le.getCause() != null ? le.getCause() : le);
    }

ConsumerContextFilter invoke execution method

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        // 用RpcContext存储调用的上下文信息
        RpcContext.getContext()
                .setInvoker(invoker)
                .setInvocation(invocation)
                .setLocalAddress(NetUtils.getLocalHost(), 0)
                .setRemoteAddress(invoker.getUrl().getHost(),
                        invoker.getUrl().getPort());
        if (invocation instanceof RpcInvocation) {
            ((RpcInvocation) invocation).setInvoker(invoker);
        }
        try {
            // (详见下文)
            RpcResult result = (RpcResult) invoker.invoke(invocation);
            RpcContext.getServerContext().setAttachments(result.getAttachments());
            return result;
        } finally {
            // 清除上下文RpcContext中存储的“附件”信息
            RpcContext.getContext().clearAttachments();
        }
    }

FutureFilter invoke execution method

    @Override
    public Result invoke(final Invoker<?> invoker, final Invocation invocation) throws RpcException {
        // 此次调用是否是异步调用
        final boolean isAsync = RpcUtils.isAsync(invoker.getUrl(), invocation);

        fireInvokeCallback(invoker, invocation);
        // need to configure if there's return value before the invocation in order to help invoker to judge if it's
        // necessary to return future.
        Result result = invoker.invoke(invocation);
        if (isAsync) {
            // 异步调用
            asyncCallback(invoker, invocation);
        } else {
            // 同步调用(默认)
            syncCallback(invoker, invocation, result);
        }
        return result;
    }

ListenerInvokerWrapper invoke execution method

    @Override
    public Result invoke(Invocation invocation) throws RpcException {
        // 代码比较简单,没啥好说的
        return invoker.invoke(invocation);
    }

AbstractInvoker invoke execution method

    @Override
    public Result invoke(Invocation inv) throws RpcException {
        // if invoker is destroyed due to address refresh from registry, let's allow the current invoke to proceed
        // 上面的注释是指,在调用过程中如果invoker被更新了,则打印一个warn,并允许当前调用继续执行
        if (destroyed.get()) {
            logger.warn("Invoker for service " + this + " on consumer " + NetUtils.getLocalHost() + " is destroyed, "
                    + ", dubbo version is " + Version.getVersion() + ", this invoker should not be used any longer");
        }

        RpcInvocation invocation = (RpcInvocation) inv;
        invocation.setInvoker(this);
        // debug之后可以看到,attachment中存放的是接口信息
        if (attachment != null && attachment.size() > 0) {
            invocation.addAttachmentsIfAbsent(attachment);
        }
        Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
        if (contextAttachments != null && contextAttachments.size() != 0) {
            /**
             * invocation.addAttachmentsIfAbsent(context){@link RpcInvocation#addAttachmentsIfAbsent(Map)}should not be used here,
             * because the {@link RpcContext#setAttachment(String, String)} is passed in the Filter when the call is triggered
             * by the built-in retry mechanism of the Dubbo. The attachment to update RpcContext will no longer work, which is
             * a mistake in most cases (for example, through Filter to RpcContext output traceId and spanId and other information).
             */
            invocation.addAttachments(contextAttachments);
        }
        // 获取本次调用是否是异步,如果是异步,则在invocation对象中设置该属性
        if (getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)) {
            invocation.setAttachment(Constants.ASYNC_KEY, Boolean.TRUE.toString());
        }
        // 代码跟进去不难发现,如果是异步调用,会将INVOKE_ID加入到附件中一并发送至服务提供方,在返回时,才能用INVOKE_ID匹配request
        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
        try {
             // 发起调用(详见下文)
            return doInvoke(invocation);
        } catch (InvocationTargetException e) { // biz exception  调用异常处理
            Throwable te = e.getTargetException();
            if (te == null) {
                return new RpcResult(e);
            } else {
                if (te instanceof RpcException) {
                    ((RpcException) te).setCode(RpcException.BIZ_EXCEPTION);
                }
                return new RpcResult(te);
            }
        } catch (RpcException e) {
            if (e.isBiz()) {
                return new RpcResult(e);
            } else {
                throw e;
            }
        } catch (Throwable e) {
            return new RpcResult(e);
        }
    }

DubboInvoker execution method doInvoke

    @Override
    protected Result doInvoke(final Invocation invocation) throws Throwable {
        RpcInvocation inv = (RpcInvocation) invocation;
        final String methodName = RpcUtils.getMethodName(invocation);
        // 设置调用信息
        inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());
        inv.setAttachment(Constants.VERSION_KEY, version);
        
        // 获取客户端ExchangeClient信息(用来发送调用请求),包括url、client、refenceCount、ghostClientMap属性
        // url:中包记录dubbo、该provider的详细信息(version,interface,心跳频率等等)
        // client:服务器的信息(消费者和提供者)、长连接维持心跳的schedule线程池,此时也能看出dubbo是用netty构建的长连接
        ExchangeClient currentClient;
        if (clients.length == 1) {
            currentClient = clients[0];
        } else {
            currentClient = clients[index.getAndIncrement() % clients.length];
        }
        try {
            boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);// 是否异步
            boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);// 该项配置是指,本次调用是否为单向调用
            int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
            if (isOneway) {
                boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
                currentClient.send(inv, isSent);// 如果是单向调用,则send,本次调用结束
                RpcContext.getContext().setFuture(null);
                return new RpcResult();
            } else if (isAsync) {
                ResponseFuture future = currentClient.request(inv, timeout);
                RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
                return new RpcResult();
            } else {
                RpcContext.getContext().setFuture(null);
                // 同步则request,并返回一个ResponseFuture对象,执行get获取响应对象result
                // 通过Condition.await()方法等待provider响应,待响应到达后,执行condition.signal()方法
                // request代码详见下文
                return (Result) currentClient.request(inv, timeout).get();
            }
        } catch (TimeoutException e) {
            throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "Invoke remote method timeout. method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
        } catch (RemotingException e) {
            throw new RpcException(RpcException.NETWORK_EXCEPTION, "Failed to invoke remote method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }

HeaderExchangeChannel method of execution request

    @Override
    public ResponseFuture request(Object request, int timeout) throws RemotingException {
        // 如果该channel被关闭则抛异常
        if (closed) {
            throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!");
        }
        // create request.   创建request对象并作包装
        Request req = new Request();
        req.setVersion(Version.getProtocolVersion());
        req.setTwoWay(true);// 设置为双向(本次调用需要有响应)
        req.setData(request);
        // 注册future,用于获取响应
        DefaultFuture future = new DefaultFuture(channel, req, timeout);
        try {
            channel.send(req);
        } catch (RemotingException e) {
            future.cancel();// 发生异常则cancel
            throw e;
        }
        return future;
    }

NettyChannel send execution method

    @Override
    public void send(Object message, boolean sent) throws RemotingException {
        super.send(message, sent);// 判断该channel是否被关闭

        boolean success = true;
        int timeout = 0;
        try {
            ChannelFuture future = channel.write(message);// 发送信息
            if (sent) {
                 // 获取超时时间,默认1s
                timeout = getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
                success = future.await(timeout);// 等待响应
            }
            Throwable cause = future.getCause();// 异常处理
            if (cause != null) {
                throw cause;
            }
        } catch (Throwable e) {
            throw new RemotingException(this, "Failed to send message " + message + " to " + getRemoteAddress() + ", cause: " + e.getMessage(), e);
        }

        if (!success) {
            throw new RemotingException(this, "Failed to send message " + message + " to " + getRemoteAddress()
                    + "in timeout(" + timeout + "ms) limit");
        }
    }

End call

In summary, the entire dubboconsumer calling process, several key classes is very important.

  • com.alibaba.dubbo.rpc.RpcInvocation
  1. Each call will be converted to the object, and the object is gradually enriched during the call, the call chain multiple use of the above-described setAttachmentsmethod of recording information.
  2. See source, is to achieve such Serializablean interface, and at the end of HeaderExchangeChanneltransmission requestwhen the object is setDatain.
  • com.alibaba.dubbo.rpc.Invoker(Contains many subclasses)
    each providerafter the service registration is completed, will be in each consumergeneration in such an invokerobject when the tracking code is not uncommon to this object, almost throughout the call chain, then what role do? It is how to design it?
  1. If you carefully read the above article, it should be difficult to find in a variety invokerof time with the code, there is always time to time and came out another invoker, such as: DubboInvoker, FailbackClusterInvokerand MockClusterInvoker(the class using static agent to achieve), and so on. why? Each class in both their business significance, such as whether to use mockfeatures such as pick ExchangeClientto send operations, such as get the number of retries performed retry loop.
  2. Careful junior partner should be able to AbstractClusterInvokersee that there is such a code in the class List<Invoker<T>> invokers = list(invocation);, which invokeris actually from the directoryacquisition of (another important category, a detailed description below), it is how to maintain?
  • com.alibaba.dubbo.rpc.cluster.Directory(Subclass AbstractDirectory, RegistryDirectory)
    personally think that this class is the whole dubbocall chain in the most important category, which carries a lot of useful information.
  1. RegistryDirectoryClass methodInvokerMapobjects, the map in cacheall dubboprotocols service methodand invokercorrespondence information, said acquisition above invokeris obtained from the map in.
  2. AbstractDirectoryThe routersobject is dubboto provide route security, specifically if you use (subsequent re-study)
  3. There are urlInvokerMap, routerFactory, configuratorFactoryand so on, how to use these attributes readers look at the code it yourself herein, this is not described.

to sum up

Source Dubbo temporarily call it to the consumer side of this, I hope to be useful to the reader, follow-up will continue to study the source code, the underlying principles to understand the most, saw a lot of source code, write their own too good, or in the design of the program more or less can also learn some upgrade themselves.

Guess you like

Origin blog.csdn.net/red_sheeps/article/details/85649067
Recommended