文章目录

  • dubbo的调用过程
  • 服务调用方式
  • MockClusterInvoker.invoke
  • AbstractClusterInvoker.invoke
  • FailoverClusterInvoker.doInvoke
  • 负载均衡选择invoker,select->doSelect方法
  • AbstractInvoker.invoke->DubboInvoker.doInvoke
  • HeaderExchangeChannel.request
  • DefaultFuture
  • 总结一下


dubbo的调用过程

dubbo 服务架构 dubbo服务之间是如何调用的_dubbo 服务架构

首先服务消费者通过代理对象 Proxy 发起远程调用,接着通过网络客户端 Client 将编码后的请求发送给服务提供方的网络层上,也就是 Server。Server 在收到请求后,首先要做的事情是对数据包进行解码。然后将解码后的请求发送至分发器 Dispatcher,再由分发器将请求派发到指定的线程池上,最后由线程池调用具体的服务。这就是一个远程调用请求的发送与接收过程。至于响应的发送与接收过程,这张图中没有表现出来。

服务调用方式

dubbo的调用主要分为三种,同步调用,异步有返回值调用,异步无返回值调用,默认是同步调用.
dubbo消费端是生成的代理类,前面讲spring代理类的时候讲过,

每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。

dubbo服务引用的时候讲过代理类是通过JavassistProxyFactory生成的,内部的InvocationHandler实际就是InvokerInvocationHandler.

dubbo 服务架构 dubbo服务之间是如何调用的_List_02


InvokerInvocationHandler方法比较简单,就是调用 InvocationHandler 接口实现类的 invoke 方法:

public class InvokerInvocationHandler implements InvocationHandler {
    private static final Logger logger = LoggerFactory.getLogger(InvokerInvocationHandler.class);
    private final Invoker<?> invoker;

    public InvokerInvocationHandler(Invoker<?> handler) {
        this.invoker = handler;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //method  调用的目标方法
        //args 目标方法的参数
        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]);
        }

        return invoker.invoke(createInvocation(method, args)).recreate();
    }

    private RpcInvocation createInvocation(Method method, Object[] args) {
        RpcInvocation invocation = new RpcInvocation(method, args);
        if (RpcUtils.hasFutureReturnType(method)) {
            invocation.setAttachment(Constants.FUTURE_RETURNTYPE_KEY, "true");
            invocation.setAttachment(Constants.ASYNC_KEY, "true");
        }
        return invocation;
    }

}

MockClusterInvoker.invoke

dubbo自身是支持mock服务的,在reference标签里,有一个参数mock,该参数有四个值,false,default,true,或者Mock类的类名。
分别代表如下含义:
false,不调用mock服务。
true,当服务调用失败时,使用mock服务。
default,当服务调用失败时,使用mock服务。
force,强制使用Mock服务(不管服务能否调用成功)。(使用xml配置不生效,使用ReferenceConfigAPI可以生效)
使用方法:
将mock参数启用,在dubbo:reference中添加参数项mock=true

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

        String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim();
        if (value.length() == 0 || value.equalsIgnoreCase("false")) {
            //no mock
            // 不走mock
            result = this.invoker.invoke(invocation);
        } else if (value.startsWith("force")) {
            // 强制走本地返回
            if (logger.isWarnEnabled()) {
                logger.warn("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + directory.getUrl());
            }
            //force:direct mock
            // 强制执行mock
            result = doMockInvoke(invocation, null);
        } else {
            //fail-mock
            try {
                result = this.invoker.invoke(invocation);
            } catch (RpcException e) {
                if (e.isBiz()) {
                    throw e;
                }
                
                if (logger.isWarnEnabled()) {
                    logger.warn("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + directory.getUrl(), e);
                }
                // 调用mock返回
                result = doMockInvoke(invocation, e);
            }
        }
        return result;
    }

AbstractClusterInvoker.invoke

下一个invoke是FailoverClusterInvoke,但是在这里它又用到了模版方法,所以直接进入到父类AbstractClusterInvoker的invoke方法中.然后一些具体的操作放在了抽象方法doInvoke里面.
稍微总结一下就是 AbstractClusterInvoker 拿到 Directory 返回的 Invoker 列表,生成负载均衡器LoadBalance ,然后调用子类的doInvoke进行方法调用.

@Override
    public Result invoke(final Invocation invocation) throws RpcException {
        //检查 连接是否被销毁
        checkWhetherDestroyed();

        // binding attachments into invocation.
        // attachments -> 隐式传参
        //RpcContext.getContext().setAttachment("key","value");
        //绑定attachments,Dubbo中,可以通过 RpcContext 上的 setAttachment 和 getAttachment 在 服务消费方和提供方之间进行参数的隐式传递,所以这段代码中会去绑定attachments
        Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
        if (contextAttachments != null && contextAttachments.size() != 0) {
            ((RpcInvocation) invocation).addAttachments(contextAttachments);
        }

        /**
         *  去哪里拿到所有的目标服务呢?(RegistryDirectory)
         *  route 路由
         *  invokers.size = 2 ->
         * 通过路由过滤从 RegistryDirectory对象中拿到符合条件的 invokers
         */
        //通过list获得invoker列表,这个列表基本可以猜测到是从directory里面获得的、但是这里面还实现 了服务路由的逻辑,
        // 简单来说就是先拿到invoker列表,然后通过router进行服务路由,筛选出符 合路由规则的服务提供者
        List<Invoker<T>> invokers = list(invocation);

        // 初始化负载均衡的机制 通过spi机制获取实例 默认 RandomLoadBalance
        LoadBalance loadbalance = initLoadBalance(invokers, invocation);
        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
        // 抽象方法,由子类实现
        return doInvoke(invocation, invokers, loadbalance);
    }

FailoverClusterInvoker.doInvoke

FailoverClusterInvoker就是失败重试invoker,所以这里面应该会实现重试的逻辑,就是获得重试次数,同时记录请求过得invoker,然后进行循环调用,如果出现业务异常则直接返回,否则接着循环.

public class FailoverClusterInvoker<T> extends AbstractClusterInvoker<T> {

    private static final Logger logger = LoggerFactory.getLogger(FailoverClusterInvoker.class);

    public FailoverClusterInvoker(Directory<T> directory) {
        super(directory);
    }

    @Override
    @SuppressWarnings({"unchecked", "rawtypes"})
    public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
        List<Invoker<T>> copyInvokers = invokers;
        checkInvokers(copyInvokers, invocation);
        String methodName = RpcUtils.getMethodName(invocation);
        // 重试次数,默认2
        int len = getUrl().getMethodParameter(methodName, Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES) + 1;
        if (len <= 0) {
            len = 1;
        }
        // retry loop.
        RpcException le = null; // last exception.
        //invoked ->表示调用过的服务(记录调用过的服务)
        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
                checkInvokers(copyInvokers, invocation);
            }
            //select -> 通过负载均衡算法之后,得到一个真正的目标invoker
            Invoker<T> invoker = select(loadbalance, invocation, copyInvokers, invoked);
            invoked.add(invoker);
            RpcContext.getContext().setInvokers((List) invoked);
            try {
                // 执行invoke调用
                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);
    }

}

负载均衡选择invoker,select->doSelect方法

通过loadbalance的select方法选出要调用的invoker,然后判断是否调用过,前面失败重试记录的.

dubbo 服务架构 dubbo服务之间是如何调用的_List_03

AbstractInvoker.invoke->DubboInvoker.doInvoke

AbstractInvoker也是模板方法,主要就是一些参数塞到Invocation里面.没啥看的,主要方法是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);

        //初始化invoker的时候,构建的一个远程通信连接
        ExchangeClient currentClient;
        if (clients.length == 1) {
            // 默认
            currentClient = clients[0];
        } else {
            //通过取模获得其中一个连接
            currentClient = clients[index.getAndIncrement() % clients.length];
        }
        try {
            boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
            boolean isAsyncFuture = RpcUtils.isReturnTypeFuture(inv);
            //表示当前的方法是否存在返回值
            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);
                RpcContext.getContext().setFuture(null);
                return new RpcResult();
            } else if (isAsync) {
                // 异步有返回值
                ResponseFuture future = currentClient.request(inv, timeout);
                // For compatibility
                FutureAdapter<Object> futureAdapter = new FutureAdapter<>(future);
                RpcContext.getContext().setFuture(futureAdapter);

                Result result;
                if (isAsyncFuture) {
                    // register resultCallback, sometimes we need the async result being processed by the filter chain.
                    result = new AsyncRpcResult(futureAdapter, futureAdapter.getResultFuture(), false);
                } else {
                    result = new SimpleAsyncRpcResult(futureAdapter, futureAdapter.getResultFuture(), false);
                }
                return result;
            } else {
                // 同步
                RpcContext.getContext().setFuture(null);
                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);
        }
    }

实际调用是 ResponseFuture future = currentClient.request(inv, timeout);
返回参数ResponseFuture的实现类是DefaultFuture,DefaultFuture里面通过唯一id解决异步调用返回到对应的feature的问题.
而currentClient.request链路是:ReferenceCountExchangeClient->HeaderExchangeClient->HeaderExchangeChannel->(request方 法)

HeaderExchangeChannel.request

@Override
    public ResponseFuture request(Object request) throws RemotingException {
        return request(request, channel.getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT));
    }

    @Override
    public ResponseFuture request(Object request, int timeout) throws RemotingException {
        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);
        DefaultFuture future = DefaultFuture.newFuture(channel, req, timeout);
        try {
            channel.send(req);
        } catch (RemotingException e) {
            future.cancel();
            throw e;
        }
        return future;
    }

DefaultFuture

如果是异步的话,怎么找到对应的Future呢?
通过 Request 生成的唯一id-DefaultFuture存到ConcurrentHashMap,然后服务端返回的时候也返回对应的唯一 ID。

// 组装一个request
Request req = new Request();

在初始化Request的时候会初始化个唯一id,就是自增id.

dubbo 服务架构 dubbo服务之间是如何调用的_dubbo 服务架构_04

private DefaultFuture(Channel channel, Request request, int timeout) {
        this.channel = channel;
        this.request = request;
        this.id = request.getId();
        this.timeout = timeout > 0 ? timeout : channel.getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
        // put into waiting map.
        FUTURES.put(id, this);
        CHANNELS.put(id, channel);
    }

DefaultFuture继承ResponseFuture,在DubboInvoker中,同步返回的时候是直接调用DefaultFuture的get方法返回.而异步方法则是将DefaultFuture封装为FutureAdapter,FutureAdapter内部维护了DefaultFuture和CompletableFuture,又将FutureAdapter封装到AsyncRpcResult返回.代码比较简单,可以自己看看.

dubbo 服务架构 dubbo服务之间是如何调用的_ide_05


get方法,通过lock实现,

@Override
    public Object get() throws RemotingException {
        return get(timeout);
    }

    @Override
    public Object get(int timeout) throws RemotingException {
        if (timeout <= 0) {
            timeout = Constants.DEFAULT_TIMEOUT;
        }
        if (!isDone()) {
            long start = System.currentTimeMillis();
            lock.lock();
            try {
                while (!isDone()) {
                    done.await(timeout, TimeUnit.MILLISECONDS);
                    if (isDone() || System.currentTimeMillis() - start > timeout) {
                        break;
                    }
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();
            }
            if (!isDone()) {
                throw new TimeoutException(sent > 0, channel, getTimeoutMessage(false));
            }
        }
        return returnFromResponse();
    }


    private Object returnFromResponse() throws RemotingException {
        Response res = response;
        if (res == null) {
            throw new IllegalStateException("response cannot be null");
        }
        if (res.getStatus() == Response.OK) {
            return res.getResult();
        }
        if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) {
            throw new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage());
        }
        throw new RemotingException(channel, res.getErrorMessage());
    }

总结一下

proxy0#sayHello(String)
  —> InvokerInvocationHandler#invoke(Object, Method, Object[])
    —> MockClusterInvoker#invoke(Invocation)
      —> AbstractClusterInvoker#invoke(Invocation)
        —> FailoverClusterInvoker#doInvoke(Invocation, List<Invoker<T>>, LoadBalance)
          —> Filter#invoke(Invoker, Invocation)  // 包含多个 Filter 调用
            —> ListenerInvokerWrapper#invoke(Invocation) 
              —> AbstractInvoker#invoke(Invocation) 
                —> DubboInvoker#doInvoke(Invocation)
                  —> ReferenceCountExchangeClient#request(Object, int)
                    —> HeaderExchangeClient#request(Object, int)
                      —> HeaderExchangeChannel#request(Object, int)
                        —> AbstractPeer#send(Object)
                          —> AbstractClient#send(Object, boolean)
                            —> NettyChannel#send(Object, boolean)
                              —> NioClientSocketChannel#write(Object)