Dubbo源码分析之四:客户端服务调用过程

目录

1. 分析思路

invoke执行链路

1. InvokerInvocationHandler.invoke

2. MockClusterInvoker.invoke

3. AbstractClusterInvoker.invoke()

4. FailoverClusterInvoker.doInvoke()

5. InvokerWrapper.invoke

6. ListenerInvokerWrapper.invoke

7.ProtocolFilterWrapper.CallbackRegistrationInvoker.invoke

8. ProtocolFilterWrapper的buildInvokerChain方法中的invoker实例的invoke方法

8.1 ConsumerContextFilter

8.2 FutureFilter.invoke

8.3 MonitorFilter.invoke

9. AsyncToSyncInvoker.invoke

10. AbstractInvoker.invoke

11、DubboInvoker.doInvoke()

发送请求过程

1. ReferenceCountExchangeClient.request;

2. HeaderExchangeClient.request

3. HeaderExchangeChannel.request

3.1 封装Request

3.2 构建DefaultFuture

3.3 调用超时定时任务

4. AbstractClient.send

5. NettyChannel.send

6. ChannelHandler执行链路

接收服务端响应

1. 解码链路

2. NettyClientHandler.channelRead()

3. ChannelEventRunnable线程链路

处理请求响应结果:recreate()

调用过程中的异步总结


1. 分析思路

我们以下面的Demo来分析服务调用的整个过程。

    public static void main(String[] args) throws IOException {
        ReferenceConfig<DemoService> reference = new ReferenceConfig<>();
        reference.setApplication(new ApplicationConfig("dubbo-demo-api-consumer"));
        reference.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181"));
        reference.setInterface(DemoService.class);
//        reference.setCheck(false);
        DemoService service = reference.get();
        String message = service.sayHello("dubbo");
        System.out.println(message);
        System.in.read();
    }

通过获取服务接口的代理类Proxy,然后发起远程调用。

我们将发起调用的过程拆分为4个过程来分析:

1、进入invoke调用链

2、发送请求

3、接收服务端响应

4、处理响应结果

invoke执行链路

代理类调用方法时实际是交给了InvokerInvocationHandler.invoke()方法,这里也是invoke执行链的开始。

整体invoke的执行链路时序图:

image

1. InvokerInvocationHandler.invoke

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        ...
        ...
        //实际调用服务接口的入口
        return invoker.invoke(new RpcInvocation(method, args)).recreate();
    }

主要做了两件事情:

1、将调用的方法和参数封装成RpcInvocation作为Rpc调用过程中的参数。这个RpcInvocation代表的是调用过程中的会话域。

2、执行invoker.invoke方法

InvokerInvocationHandler 中的 invoker 成员变量类型为 MockClusterInvoker,因此我们接着看MockClusterInvoker。

2. MockClusterInvoker.invoke

封装了服务降级的逻辑,我们看代码:

 public Result invoke(Invocation invocation) throws RpcException {
        Result result = null;
        //获取mock参数
        String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), MOCK_KEY, Boolean.FALSE.toString()).trim();
        //mock参数不存在,则不走服务降级逻辑
        if (value.length() == 0 || "false".equalsIgnoreCase(value)) {
            //no mock
            result = this.invoker.invoke(invocation);
        } else if (value.startsWith("force")) {
           // force:xxx 直接执行 mock 逻辑,不发起远程调用
            result = doMockInvoke(invocation, null);
        } else {
           // fail:xxx 表示消费方对调用服务失败后,再执行 mock 逻辑,不抛出异常
            try {
                result = this.invoker.invoke(invocation);
                if(result.getException() != null && result.getException() instanceof RpcException){
                    RpcException rpcException= (RpcException)result.getException();
                    if(rpcException.isBiz()){
                        throw  rpcException;
                    }else {
                        result = doMockInvoke(invocation, rpcException);
                    }
                }

            } catch (RpcException e) {
                if (e.isBiz()) {
                    throw e;
                }
                result = doMockInvoke(invocation, e);
            }
        }
        return result;
    }

关于服务降级的逻辑这里不分析,后面单独分析

3. AbstractClusterInvoker.invoke()

封装了集群失败重试的容错的公共逻辑,也就是当集群环境某个节点调用失败之后,如何选择重试策略,doInvoke由不同容错策略的子类实现。


    public Result invoke(final Invocation invocation) throws RpcException {
        checkWhetherDestroyed();
        // 获取上下文Rpc参数并封装到invocation中
        Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
        if (contextAttachments != null && contextAttachments.size() != 0) {
            ((RpcInvocation) invocation).addAttachments(contextAttachments);
        }
        //列举invoker
        List<Invoker<T>> invokers = list(invocation);
        //获取负载均衡策略
        LoadBalance loadbalance = initLoadBalance(invokers, invocation);
        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
        return doInvoke(invocation, invokers, loadbalance);
    }

1、根据RegistryDirectory.list方法获取服务列表invokers;

2、获取负载均衡策略LoadBalance,默认为RandomLoadBalance

3、选择具体的集群策略执行doInvoke。

4. FailoverClusterInvoker.doInvoke()

Failover Cluster是dubbo默认的容错策略:失败自动切换。

      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);
        //获取重试次数参数
        int len = getUrl().getMethodParameter(methodName, RETRIES_KEY, DEFAULT_RETRIES) + 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++) {
            //当发生重试的时候,重新调用list方法,获取最新的服务列表
            if (i > 0) {
                checkWhetherDestroyed();
                copyInvokers = list(invocation);
                // check again
                checkInvokers(copyInvokers, invocation);
            }
            //通过负载均衡策略从上面获取的invoker列表获取一个可用的invoker
            Invoker<T> invoker = select(loadbalance, invocation, copyInvokers, invoked);
            invoked.add(invoker);
            //设置 invoked 到 RPC 上下文中
            RpcContext.getContext().setInvokers((List) invoked);
            try {
                // 调用目标 Invoker 的 invoke 方法
                Result result = invoker.invoke(invocation);
                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()
    }

总结下大概做了哪些事:

1、获取服务列表(也就是invoker集合);

2、获取负载均衡策略LoadBalance(默认是RandomLoadBalance)

3、获取重试次数参数

4、通过负载均衡策略从上面获取的invoker列表中获取一个可用的invoker;

5、通过获取到的invoker调用invoke方法,这里的invoker是RegistryDirectory.InvokerDelegate的实例。

6、如果调用失败,会重新循环调用,调用前会重新获取服务列表(防止服务列表有服务不可用),循环次数就是第三步获取的重试次数参数

关于第一步获取服务列表invokers和第四步根据负载均衡策略从invokers中选择一个invoker的逻辑,后面单独来分析。

5. InvokerWrapper.invoke

Invoker的包装类,但实际包装之后未做任何而外逻辑,因此继续往下看。

public class InvokerWrapper<T> implements Invoker<T> {

    private final Invoker<T> invoker;

    private final URL url;

    public InvokerWrapper(Invoker<T> invoker, URL url) {
        this.invoker = invoker;
        this.url = url;
    }
    ...
    ...
    @Override
    public Result invoke(Invocation invocation) throws RpcException {
        return invoker.invoke(invocation);
    }

6. ListenerInvokerWrapper.invoke

ListenerInvokerWrapper主要作用是在构造invoker和销毁invoker增加了回调监听器的逻辑处理。

不过这里dubbo默认并未提供任何InvokerListener,实际执行invoke时也无任何逻辑增强。

7.ProtocolFilterWrapper.CallbackRegistrationInvoker.invoke

1、进入所有拦截器的调用链中。

2、调用拦截器invoker之后,将返回的结果通过每个拦截器实现了Listener的方法进行异步回调通知。

 static class CallbackRegistrationInvoker<T> implements Invoker<T> {

        private final Invoker<T> filterInvoker;
        private final List<Filter> filters;

        public CallbackRegistrationInvoker(Invoker<T> filterInvoker, List<Filter> filters) {
            this.filterInvoker = filterInvoker;
            this.filters = filters;
        }

        @Override
        public Result invoke(Invocation invocation) throws RpcException {
            //调用拦截器链的invoke
            Result asyncResult = filterInvoker.invoke(invocation);
            //把异步返回的结果加入到上下文中
            asyncResult = asyncResult.whenCompleteWithContext((r, t) -> {
                //循环各个过滤器
                for (int i = filters.size() - 1; i >= 0; i--) {
                    Filter filter = filters.get(i);
                    //如果该Filter属于ListenableFilter,则执行listener的回调逻辑
                    if (filter instanceof ListenableFilter) {
                        Filter.Listener listener = ((ListenableFilter) filter).listener();
                        if (listener != null) {
                            if (t == null) {
                          //调用回调方法onResponse
                            listener.onResponse(r, filterInvoker, invocation);
                            } else {
                             
                            listener.onError(t, filterInvoker, invocation);
                            }
                        }
                    } else {
                    //这个是为了兼容2.7版本之前的逻辑
                        filter.onResponse(r, filterInvoker, invocation);
                    }
                }
            });
            return asyncResult;
        }
        ...
        ...
    }

8. ProtocolFilterWrapper的buildInvokerChain方法中的invoker实例的invoke方法

这里是经过一系列过滤器的开始。 该方法中是对异常的捕获,调用内部类Listener的onError来回调错误信息。接下来看它经过了哪些拦截器。

 private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
        Invoker<T> last = invoker;
        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);

        if (!filters.isEmpty()) {
            for (int i = filters.size() - 1; i >= 0; i--) {
                final Filter filter = filters.get(i);
                final Invoker<T> next = last;
                last = new Invoker<T>() {
                    ...
                    ...
                    @Override
                    public Result invoke(Invocation invocation) throws RpcException {
                        Result asyncResult;
                        try {
                            // 依次调用各个过滤器,获得最终的返回结果
                            asyncResult = filter.invoke(next, invocation);
                        } catch (Exception e) {
                            // onError callback
                            if (filter instanceof ListenableFilter) {
                                Filter.Listener listener = ((ListenableFilter) filter).listener();
                                if (listener != null) {
                                    listener.onError(e, invoker, invocation);
                                }
                            }
                            throw e;
                        }
                        return asyncResult;
                    }
                };
            }
        }

        return new CallbackRegistrationInvoker<>(last, filters);
    }

我们看下默认经过了哪些Filter

8.1 ConsumerContextFilter

在当前的RpcContext中记录本次调用的一次状态信息,这里注意在finally中会调用removeContext,也就验证了RpcContext记录的上下文仅仅只针对一次调用有效。

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        //将invoker、invocation、本地地址、远程地址等放到上下文
        RpcContext.getContext()
                .setInvoker(invoker)
                .setInvocation(invocation)
                .setLocalAddress(NetUtils.getLocalHost(), 0)
                .setRemoteAddress(invoker.getUrl().getHost(), invoker.getUrl().getPort())
                .setRemoteApplicationName(invoker.getUrl().getParameter(REMOTE_APPLICATION_KEY))
                .setAttachment(REMOTE_APPLICATION_KEY, invoker.getUrl().getParameter(APPLICATION_KEY));
        if (invocation instanceof RpcInvocation) {
            ((RpcInvocation) invocation).setInvoker(invoker);
        }
        try {
            RpcContext.removeServerContext();
            //调用下一个过滤器
            return invoker.invoke(invocation);
        } finally {
            RpcContext.removeContext();
        }
    }

该过滤器执行完成后,会回到上一步,循环下一个过滤器调用:FutureFilter

8.2 FutureFilter.invoke

处理dubbo的dubbo:method配置的onreturn、onthrow、oninvoke逻辑。

具体参考:事件通知

    public Result invoke(final Invoker<?> invoker, final Invocation invocation) throws RpcException {
        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.
        return invoker.invoke(invocation);
    }

8.3 MonitorFilter.invoke

    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        // 如果设置了监控
        if (invoker.getUrl().hasParameter(MONITOR_KEY)) {
            invocation.setAttachment(MONITOR_FILTER_START_TIME, String.valueOf(System.currentTimeMillis()));
            getConcurrent(invoker, invocation).incrementAndGet(); // count up
        }
            return invoker.invoke(invocation); // proceed invocation chain
    }

该过滤器实际用来做监控,监控服务的调用数量。

9. AsyncToSyncInvoker.invoke

把异步结果转化为同步结果。

执行完invoke获取到Result之后,根据会话域中设置的invokeMode枚举,如果是同步调用,则会在这个类中进行异步结果转同步的处理(本质就是调用CompletableFuture.get方法)。

关于invokeMode的设置是在下一步AbstractInvoker.invoke执行invoke前设置的。

    public Result invoke(Invocation invocation) throws RpcException {
        Result asyncResult = invoker.invoke(invocation);

        try {
            // 如果是同步的调用
            if (InvokeMode.SYNC == ((RpcInvocation) invocation).getInvokeMode()) {
                //从异步结果中get结果
                asyncResult.get(Integer.MAX_VALUE, TimeUnit.MILLISECONDS);
            }
        } catch (InterruptedException e) {
            throw new RpcException("Interrupted unexpectedly while waiting for remoting result to return!  method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
        } catch (ExecutionException e) {
            Throwable t = e.getCause();
            if (t instanceof TimeoutException) {
                throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "Invoke remote method timeout. method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
            } else if (t instanceof RemotingException) {
                throw new RpcException(RpcException.NETWORK_EXCEPTION, "Failed to invoke remote method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
            }
        } catch (Throwable e) {
            throw new RpcException(e.getMessage(), e);
        }
        return asyncResult;
    }

10. AbstractInvoker.invoke

这里主要是不同协议的一个公共逻辑,主要是会话域中封装一些值。

例如:附加值、invokeMode、异步调用id。这里重点说明下invokeMode,也就是上一步执行完invoke需要拿来判断是否要做异步转同步的逻辑。

invokeMode存在3个枚举值:

  • FUTURE:如果服务接口定义的返回参数是CompletableFuture,则属于FUTURE模式,FUTURE模式也属于Dubbo提供的一种异步调用方式(2.7版本提供,服务端异步方式)。
  • ASYNC:如果consumer对服务接口指定了ASYNC,则属于ASYNC异步调用
  • SYNC:默认值,同步调用。

这个invokeMode值除了上一步异步转同步中用到之外,还会在后面的处理响应结果是否为future的recreate方法中会用到。

    @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
        if (destroyed.get()) {
            logger.warn("Invoker for service...");
        }
        //在会话域中加入该调用链
        RpcInvocation invocation = (RpcInvocation) inv;
        invocation.setInvoker(this);
        //把附加值放入会话域
        if (CollectionUtils.isNotEmptyMap(attachment)) {
            invocation.addAttachmentsIfAbsent(attachment);
        }
        //把上下文的附加值也放入会话域
        Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
        if (CollectionUtils.isNotEmptyMap(contextAttachments)) {
            /**
             * 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);
        }
        //从配置中得到是什么模式的调用,一共有FUTURE、ASYNC和SYNC
        invocation.setInvokeMode(RpcUtils.getInvokeMode(url, invocation));
        // 如果调用是异步,则在会话域中添加id字段
        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);

        try {
            return doInvoke(invocation);
        } catch (InvocationTargetException e) { // biz exception
            Throwable te = e.getTargetException();
            if (te == null) {
                return AsyncRpcResult.newDefaultAsyncResult(null, e, invocation);
            } else {
                if (te instanceof RpcException) {
                    ((RpcException) te).setCode(RpcException.BIZ_EXCEPTION);
                }
                return AsyncRpcResult.newDefaultAsyncResult(null, te, invocation);
            }
        } catch (RpcException e) {
            if (e.isBiz()) {
                return AsyncRpcResult.newDefaultAsyncResult(null, e, invocation);
            } else {
                throw e;
            }
        } catch (Throwable e) {
            return AsyncRpcResult.newDefaultAsyncResult(null, e, invocation);
        }
    }

11、DubboInvoker.doInvoke()

这里主要是获取与服务端的Client对象,以及对oneway和非oneway的调用方式对结果进行不同处理,最终通过AsyncRpcResult进行返回。

  • oneway:异步无返回值的调用方式,consumer端只管发出去请求,不需要响应结果(返回一个空的AsyncRpcResult)。
  • 非oneway:异步或者同步的调用方式,在这里实际都是当作异步的方式进行的处理,然后利用CompletableFuture.whenComplete的方式得到异步通知响应结果,主线程并不需要阻塞。

关于非oneway的方式为什么都是作为异步方式来处理?是因为在前面AsyncToSyncInvoker中已经做了同步的处理。所以这里并不需要等待。

    protected Result doInvoke(final Invocation invocation) throws Throwable {
        RpcInvocation inv = (RpcInvocation) invocation;
        final String methodName = RpcUtils.getMethodName(invocation);
        // 再会话域中添加path、version字段
        inv.setAttachment(PATH_KEY, getUrl().getPath());
        inv.setAttachment(VERSION_KEY, version);
        //获取远程调用的ExchangeClient
        ExchangeClient currentClient;
        if (clients.length == 1) {
            currentClient = clients[0];
        } else {
            currentClient = clients[index.getAndIncrement() % clients.length];
        }
        try {
            //获取调用方式参数inOneway
            boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
            //调用超时时间
            int timeout = getUrl().getMethodPositiveParameter(methodName, TIMEOUT_KEY, DEFAULT_TIMEOUT);
            //isoneway:异步无返回值。
            if (isOneway) {
                boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
                currentClient.send(inv, isSent);
                //返回一个空的结果
                return AsyncRpcResult.newDefaultAsyncResult(invocation);
            } else {
                //同步调用或者异步有返回值
                AsyncRpcResult asyncRpcResult = new AsyncRpcResult(inv);
                CompletableFuture<Object> responseFuture = currentClient.request(inv, timeout);
                //调用之后异步通知结果 
                asyncRpcResult.subscribeTo(responseFuture);
                // save for 2.6.x compatibility, for example, TraceFilter in Zipkin uses com.alibaba.xxx.FutureAdapter
                FutureContext.getContext().setCompatibleFuture(responseFuture);
                return asyncRpcResult;
            }
        } 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);
        }
    }
    
    public void subscribeTo(CompletableFuture<?> future) {
        future.whenComplete((obj, t) -> {
            if (t != null) {
                this.completeExceptionally(t);
            } else {
                this.complete((Result) obj);
            }
        });
    }

到DubboInvoker,基本invoke的链路基本走完,接下来看发送请求的过程。

发送请求过程

发送请求整体时序图

image

1. ReferenceCountExchangeClient.request;

ReferenceCountExchangeClient 内部定义了一个引用计数变量 referenceCount,每当该对象被引用一次 referenceCount 都会进行自增。每当 close 方法被调用时,referenceCount 进行自减。ReferenceCountExchangeClient 内部仅实现了一个引用计数的功能,其他方法并无复杂逻辑,均是直接调用被装饰对象的相关方法。

final class ReferenceCountExchangeClient implements ExchangeClient {
    private final URL url;
    private final AtomicInteger referenceCount = new AtomicInteger(0);

    private ExchangeClient client;

    public ReferenceCountExchangeClient(ExchangeClient client) {
        this.client = client;
        //引用计数+1
        referenceCount.incrementAndGet();
        this.url = client.getUrl();
    }
    ...
    ...
    @Override
    public CompletableFuture<Object> request(Object request, int timeout) throws RemotingException {
        注解调用被包装对象的request
        return client.request(request, timeout);
    }
    }

2. HeaderExchangeClient.request

封装了心跳检测以及重连的逻辑; 对应的心跳检测和服务重连Task分别为:HeartbeatTimerTask、ReconnectTimerTask。

线程名称为:dubbo-client-idleCheck-。

不过在dubbo选择使用netty4之后已经不再使用自己实现的HeartbeatTimerTask了,而是利用了netty本身提供的心跳机制IdleStateHandler。

重连的任务ReconnectTimerTask依然保留

    public HeaderExchangeClient(Client client, boolean startTimer) {
        Assert.notNull(client, "Client can't be null");
        this.client = client;
        this.channel = new HeaderExchangeChannel(client);

        if (startTimer) {
            URL url = client.getUrl();
            //开启重连任务
            startReconnectTask(url);
            //开启心跳任务
            startHeartBeatTask(url);
        }
    }
    
    private void startHeartBeatTask(URL url) {
        //新版本这里返回false
        if (!client.canHandleIdle()) {
            AbstractTimerTask.ChannelProvider cp = () -> Collections.singletonList(HeaderExchangeClient.this);
            int heartbeat = getHeartbeat(url);
            long heartbeatTick = calculateLeastDuration(heartbeat);
            //心跳任务,默认是一分钟
            this.heartBeatTimerTask = new HeartbeatTimerTask(cp, heartbeatTick, heartbeat);
            IDLE_CHECK_TIMER.newTimeout(heartBeatTimerTask, heartbeatTick, TimeUnit.MILLISECONDS);
        }
    }

    private void startReconnectTask(URL url) {
        if (shouldReconnect(url)) {
            AbstractTimerTask.ChannelProvider cp = () -> Collections.singletonList(HeaderExchangeClient.this);
            int idleTimeout = getIdleTimeout(url);
            long heartbeatTimeoutTick = calculateLeastDuration(idleTimeout);
            //重连任务
            this.reconnectTimerTask = new ReconnectTimerTask(cp, heartbeatTimeoutTick, idleTimeout);
            IDLE_CHECK_TIMER.newTimeout(reconnectTimerTask, heartbeatTimeoutTick, TimeUnit.MILLISECONDS);
        }
    }

3. HeaderExchangeChannel.request

  • 封装Request;
  • 构建DefaultFuture;

3.1 封装Request

   public CompletableFuture<Object> 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 req = new Request();
        req.setVersion(Version.getProtocolVersion());
        req.setTwoWay(true);
        req.setData(request);
        //DefaultFuture
        DefaultFuture future = DefaultFuture.newFuture(channel, req, timeout);
        try {
            channel.send(req);
        } catch (RemotingException e) {
            future.cancel();
            throw e;
        }
        return future;
    }

3.2 构建DefaultFuture

  public static DefaultFuture newFuture(Channel channel, Request request, int timeout) {
        final DefaultFuture future = new DefaultFuture(channel, request, timeout);
        // timeout check
        timeoutCheck(future);
        return future;
    }

这里我们重点看下DefaultFuture类以及构建DefaultFuture的过程:

DefaultFuture本身继承了CompletableFuture,同时也维护了一个静态的缓存FUTURES map对象,这里后面在处理服务端响应Response的时候也会分析DefaultFuture的意义。

ublic class DefaultFuture extends CompletableFuture<Object> {
    
    private static final Map<Long, Channel> CHANNELS = new ConcurrentHashMap<>();
    
    //静态的map缓存变量
    private static final Map<Long, DefaultFuture> FUTURES = new ConcurrentHashMap<>();

    //获取当前request对象的id作为key,将当前DefaultFuture存放在缓存变量中
    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(TIMEOUT_KEY, DEFAULT_TIMEOUT);
        // put into waiting map.
        FUTURES.put(id, this);
        CHANNELS.put(id, channel);
    }
}

3.3 调用超时定时任务

注意在构建DefaultFuture之后还会启动一个定时任务线程检测调用超时。定时线程执行频率根据timeout的配置值决定,默认一秒,接口调用超时就是通过该定时任务实现的。

   private static void timeoutCheck(DefaultFuture future) {
        TimeoutCheckTask task = new TimeoutCheckTask(future.getId());
        future.timeoutCheckTask = TIME_OUT_TIMER.newTimeout(task, future.getTimeout(), TimeUnit.MILLISECONDS);
    }
    
    //定时任务
    private static class TimeoutCheckTask implements TimerTask {

        private final Long requestID;

        TimeoutCheckTask(Long requestID) {
            this.requestID = requestID;
        }

        @Override
        public void run(Timeout timeout) {
            DefaultFuture future = DefaultFuture.getFuture(requestID);
            if (future == null || future.isDone()) {
                return;
            }
            //没进if说明超时了
            // create exception response.
            Response timeoutResponse = new Response(future.getId());
            // set timeout status.
            timeoutResponse.setStatus(future.isSent() ? Response.SERVER_TIMEOUT : Response.CLIENT_TIMEOUT);
            timeoutResponse.setErrorMessage(future.getTimeoutMessage(true));
            // 构建超时的response放到DefaultFuture
            DefaultFuture.received(future.getChannel(), timeoutResponse, true);

        }
    }

接下来的调用就具体跟远程通信有关了,也就是 进入了Transport层。

4. AbstractClient.send

1、如果需要重连,则重新连接;

2、获取Channel,默认获取NettyChannel。

3、通过通道发送消息

    @Override
    public void send(Object message, boolean sent) throws RemotingException {
        //如果需要重连,则回重新进行链接
        if (needReconnect && !isConnected()) {
            connect();
        }
        Channel channel = getChannel();
        //TODO Can the value returned by getChannel() be null? need improvement.
        if (channel == null || !channel.isConnected()) {
            throw new RemotingException(this, "message can not send, because channel is closed . url:" + getUrl());
        }
        channel.send(message, sent);
    }

5. NettyChannel.send

这里是真正调用netty api向服务端写入数据的地方;

这里netty在写入数据的时候,默认是异步写入的,

这里有一个特别的参数sent来确定是否等待消息发出,这个sent的值可以在MethodConfig中配置。有两个值:

  • true:等待消息发出,消息发送失败将抛出异常。
  • false:默认值,不等待消息发出,将消息放入 IO 队列,即刻返回
    public void send(Object message, boolean sent) throws RemotingException {
        // whether the channel is closed
        super.send(message, sent);

        boolean success = true;
        int timeout = 0;
        try {
            //写入数据,发送消息,这里的channel是Netty本身的Channel类
            ChannelFuture future = channel.writeAndFlush(message);
            //默认情况下 sent = false
            if (sent) {
                // wait timeout ms
                timeout = getUrl().getPositiveParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT);
                //等待消息发出,若在规定时间没能发出,success 会被置为 false
                success = future.await(timeout);
            }
            Throwable cause = future.cause();
            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");
        }
    }

6. ChannelHandler执行链路

根据服务引用的分析,consumer端启动netty客户端时(NettyClient.open),在ChannelPipeline链中加入了4个ChannelHandler,分别是:

  • NettyCodecAdapter.InternalEncoder:编码器
  • NettyCodecAdapter.InternalDecoder:解码器
  • IdleStateHandler:心跳处理器
  • NettyClientHandler:最终的消息处理器

因此在上一步调用writeAndFlush之后,根据出站方向主要会经过NettyClientHandler和编码器NettyCodecAdapter.InternalEncoder进行编码后发送出去。

我们看下NettyClientHandler的write方法:

public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        super.write(ctx, msg, promise);
        final NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
        final boolean isRequest = msg instanceof Request;
        //ChannelPromise是ChannelFuture的子类
        //加入写入数据完成后的监听,这里主要是处理出站异常
        promise.addListener(future -> {
            try {
                if (future.isSuccess()) {
                    // if our future is success, mark the future to sent.
                    handler.sent(channel, msg);
                    return;
                }
                //发送消息如果失败后,则提供处理出站异常逻辑,构建失败的Response
                Throwable t = future.cause();
                if (t != null && isRequest) {
                    Request request = (Request) msg;
                    Response response = buildErrorResponse(request, t);
                    handler.received(channel, response);
                }
            } finally {
                NettyChannel.removeChannelIfDisconnected(ctx.channel());
            }
        });
    }

接着进入编码器,这里主要列下编码器的调用链路,不做编码的具体分析。

InternalEncoder.encode
-->DubboCountCodec.encode
    -->DubboCodec.encode
        -->ExchangeCodec.encode
            -->ExchangeCodec.encodeRequest

接收服务端响应

接收服务响应和发送消息一样也会经过指定的入站ChannelHandler,先经过解码器NettyCodecAdapter.InternalDecoder,然后经过NettyClientHandler。

image

1. 解码链路

InternalDecoder.decode()
    -->DubboCountCodec.decode()
        -->ExchangeCodec.decode()
            -->DubboCodec.decodeBody()

2. NettyClientHandler.channelRead()

NettyClientHandler.channelRead()
-->AbstractPeer.received()
    -->MultiMessageHandler.receive()
        -->HeartbeatHandler.receive()
            -->AllChannelHandler.receive()

这里主要看下AllChannelHandler.receive方法:

将接收到的响应派发给业务线程池(DubboClienthandler-服务ip:port-thread-)处理,这里其实就是解放出io线程,将处理逻辑交给业务线程,避免阻塞io线程。

  public void received(Channel channel, Object message) throws RemotingException {
        //获取客户端接收消息业务处理线程池
        //DubboClientHandler-192.168.100.12:20880-thread-xxx
        ExecutorService executor = getExecutorService();
        try {
            //将ChannelEventRunnable任务提交给线程池。
            executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message));
        } catch (Throwable t) {
        	if(message instanceof Request && t instanceof RejectedExecutionException){
        		Request request = (Request)message;
        		if(request.isTwoWay()){
        			String msg = "Server side(" + url.getIp() + "," + url.getPort() + ") threadpool is exhausted ,detail msg:" + t.getMessage();
        			Response response = new Response(request.getId(), request.getVersion());
        			response.setStatus(Response.SERVER_THREADPOOL_EXHAUSTED_ERROR);
        			response.setErrorMessage(msg);
        			channel.send(response);
        			return;
        		}
        	}
            throw new ExecutionException(message, channel, getClass() + " error when process received event .", t);
        }
    }

关于这里的派发策略,dubbo提供了5种策略,默认使用AllChannelHandler。

  • all: 所有消息都派发到线程池,包括请求,响应,连接事件,断开事件,心跳等。默认。 AllChannelHandler。
  • direct: 所有消息都不派发到线程池,全部在 IO 线程上直接执行。 WrappedChannelHandler。
  • message: 只有请求响应消息派发到线程池,其它连接断开事件,心跳等消息,直接在 IO 线程上执行。MessageOnlyChannelHandler。
  • execution: 只有请求消息派发到线程池,不含响应,响应和其它连接断开事件,心跳等消息,直接在 IO 线程上执行。 ExecutionChannelHandler。
  • connection: 在 IO 线程上,将连接断开事件放入队列,有序逐个执行,其它消息派发到线程池。ConnectionOrderedChannelHandler。

3. ChannelEventRunnable线程链路

ChannelEventRunnable.run()
    -->DecodeHandler.receive()
        -->HeaderExchangeHandler.received()
            -->HeaderExchangeHandler.handleResponse()
                -->DefaultFuture.received(channel, response);

分析到这里我们重点看下DefaultFuture.received方法逻辑。

这里结合前面分析的HeaderExchangeChannel.request,在request前会构造DefaultFuture对象,同时将当前请求对应的DefaultFuture封装到缓存FUTURES中。

这里在接收响应就是通过response的id获取对应请求时的DefaultFuture,然后将响应结果封装到DefaultFuture中,从而实现了如何将异步请求和响应一一对应。

    public static void received(Channel channel, Response response, boolean timeout) {
        try {
            // 根据调用编号从 FUTURES 集合中查找指定的 DefaultFuture 对象
            DefaultFuture future = FUTURES.remove(response.getId());
            if (future != null) {
                Timeout t = future.timeoutCheckTask;
                if (!timeout) {
                    // decrease Time
                    t.cancel();
                }
                //封装rsponse响应结果
                future.doReceived(response);
            } else {
                logger.warn("The timeout response finally returned at "
                        + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))
                        + ", response " + response
                        + (channel == null ? "" : ", channel: " + channel.getLocalAddress()
                        + " -> " + channel.getRemoteAddress()));
            }
        } finally {
            CHANNELS.remove(response.getId());
        }
    }
    
    private void doReceived(Response res) {
        if (res == null) {
            throw new IllegalStateException("response cannot be null");
        }
        if (res.getStatus() == Response.OK) {
            this.complete(res.getResult());
        } else if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) {
            this.completeExceptionally(new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage()));
        } else {
            this.completeExceptionally(new RemotingException(channel, res.getErrorMessage()));
        }
    }

到此接收服务端响应过程分析完,接下来看处理响应结果过程。

处理请求响应结果:recreate()

也就是执行完InvokerInvocationHandler.invoke()之后对AsyncRpcResult结果的处理:

AsyncRpcResult.recreate();

回过头看在DubboInvoker.doInvoker过程中,会构造好AsyncRpcResult对象,而AsyncRpcResult本身继承了CompletableFuture。会利用CompletableFuture.whenComplete的方式得到响应的结果。

    AsyncRpcResult asyncRpcResult = new AsyncRpcResult(inv);
    CompletableFuture<Object> defaultFuture = currentClient.request(inv, timeout);
    asyncRpcResult.subscribeTo(defaultFuture);

那在recreate方法中可以大概猜测其实就是帮使用者封装好从CompletableFuture中获取异步响应的结果的逻辑。

    @Override
    public Object recreate() throws Throwable {
        RpcInvocation rpcInvocation = (RpcInvocation) invocation;
        FutureAdapter future = new FutureAdapter(this);
        //把future放到Rpc上下文中
        RpcContext.getContext().setFuture(future);
        //如果属于future调用模型,则直接返回future
        if (InvokeMode.FUTURE == rpcInvocation.getInvokeMode()) {
            return future;
        }
        //对于非future模式,则获取响应结果
        return getAppResponse().recreate();
    }
    
    public Result getAppResponse() {
        try {
            //是否已经异步执行完了
            if (this.isDone()) {
                //返回结果
                return this.get();
            }
        } catch (Exception e) {
            // This should never happen;
            logger.error("Got exception when trying to fetch the underlying result from AsyncRpcResult.", e);
        }
        //还没有响应结果,则返回一个空的响应
        return new AppResponse();
    }
    
    

这里总结recreate方法处理的三个关键点:

1、封装异步响应结果future到RpcContext中;

2、如果调用方式属于future模式(服务端异步),则直接返回future对象;

对于Future模式我们在AbstractInvoker.invoke已经有过分析,对于future模式的调用,则返回交给使用者,自己通过调用future的获取结果方法来得到;

3、不属于future模式,则判断是否已经获取到响应结果。有则调用get返回结果,没有则返回空的AppResponse。

由于同步调用前面AsyncToSyncInvoker已经做了等待,因此这里一定能get到结果;而对于属于指定了async异步的调用,则需要关键点1中的RpcContext中获取future,然后自己从future中等待获取结果。

调用过程中的异步总结

image

1、在发送请求时,默认是利用了netty的异步发送,这里本质上是一次异步; NettyChannel.send(),不阻塞等待响应结果。

2、netty会将接收到的响应结果交给业务线程池(DubboClienthandler-服务ip:port-thread-),这是第二次异步(分离IO线程和业务线程)。

代码位置:AllChannelHandler.receive()。

最终将响应结果封装到DefaultFuture(也就是request对应的DefaultFuture)。

3、第二步得到的DefaultFuture会转换为AsyncRpcResult,AsyncRpcResult如何拿到DefaultFuture封装的响应结果也是通过异步获取,这里是第三次异步。

代码位置:DubboInvoker.doInvoke()。

    AsyncRpcResult asyncRpcResult = new AsyncRpcResult(inv);
    CompletableFuture<Object> defaultFuture = currentClient.request(inv, timeout);
    asyncRpcResult.subscribeTo(defaultFuture);
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值