dubbo源码分析学习 服务调用过程

一、通讯基础协议

首先远程调用需要定义协议,也就是互相约定我们要讲什么样的语言,要保证双方都能听得懂。

常见的三种协议形式

应用层一般有三种类型的协议形式,分别是:固定长度形式、特殊字符隔断形式、header+body 形式

固定长度形式:指的是协议的长度是固定的,比如100个字节为一个协议单元,那么读取100个字节之后就开始解析。

优点就是效率较高,无脑读一定长度就解析。

缺点就是死板,每次长度只能固定,不能超过限制的长度,并且短了还得填充,在 RPC 场景中不太合适,谁晓得参数啥的要多长,定长了浪费,定短了不够。

特殊字符隔断形式:其实就是定义一个特殊结束符,根据特殊的结束符来判断一个协议单元的结束,比如用换行符等等。

这个协议的优点是长度自由,反正根据特殊字符来截断,缺点就是需要一直读,直到读到一个完整的协议单元之后才能开始解析,然后假如传输的数据里面混入了这个特殊字符就出错了。

header+body 形式:也就是头部是固定长度的,然后头部里面会填写 body 的长度, body 是不固定长度的,这样伸缩性就比较好了,可以先解析头部,然后根据头部得到 body 的 len 然后解析 body。

dubbo 协议就是属于 header+body 形式,而且也有特殊的字符 0xdabb ,这是用来解决 TCP 网络粘包问题的。

Dubbo 协议

Dubbo 支持的协议很多,我们就简单的分析下 Dubbo 协议。

协议分为协议头和协议体,可以看到 16 字节的头部主要携带了魔法数,也就是之前说的 0xdabb,然后一些请求的设置,消息体的长度等等。

16 字节之后就是协议体了,包括协议版本、接口名字、接口版本、方法名字等等。

其实协议很重要,因为从中可以得知很多信息,而且只有懂了协议的内容,才能看得懂编码器和解码器在干嘛,我再截取一张官网对协议的解释图。

需要约定序列化器

网络是以字节流的形式传输的,相对于我们的对象来说,我们对象是多维的,而字节流是一维的,我们需要把我们的对象压缩成一维的字节流传输到对端。

然后对端再反序列化这些字节流变成对象。

序列化协议

其实从上图的协议中可以得知 Dubbo 支持很多种序列化,我不具体分析每一种协议,就大致分析序列化的种类,万变不离其宗。

序列化大致分为两大类,一种是字符型,一种是二进制流

字符型的代表就是 XML、JSON,字符型的优点就是调试方便,它是对人友好的,我们一看就能知道那个字段对应的哪个参数。

缺点就是传输的效率低,有很多冗余的东西,比如 JSON 的括号,对于网络传输来说传输的时间变长,占用的带宽变大

还有一大类就是二进制流型,这种类型是对机器友好的,它的数据更加的紧凑,所以占用的字节数更小,传输更快

缺点就是调试很难,肉眼是无法识别的,必须借用特殊的工具转换。

Dubbo 默认用的是 hessian2 序列化协议

所以实际落地还需要先约定好协议,然后再选择好序列化方式构造完请求之后发送。

二、调用过程

粗略的调用流程图

我们来看一下官网的图。

简述一下就是客户端发起调用,实际调用的是代理类,代理类最终调用的是 Client (默认Netty),需要构造好协议头,然后将 Java 的对象序列化生成协议体,然后网络调用传输。

服务端的 NettyServer接到这个请求之后,分发给业务线程池,由业务线程调用具体的实现方法。

但是这还不够,因为 Dubbo 是一个生产级别的 RPC 框架,它需要更加的安全、稳重。

详细的调用流程

前面已经分析过了客户端也是要序列化构造请求的,为了让图更加突出重点,所以就省略了这一步,当然还有响应回来的步骤,暂时就理解为原路返回,下文会再做分析。

可以看到生产级别就得稳,因此服务端往往会有多个,多个服务端的服务就会有多个 Invoker,最终需要通过路由过滤,然后再通过负载均衡机制来选出一个 Invoker 进行调用。

当然 Cluster 还有容错机制,包括重试等等。

请求会先到达 Netty 的 I/O 线程池进行读写和可选的序列化和反序列化,可以通过 decode.in.io控制,然后通过业务线程池处理反序列化之后的对象,找到对应 Invoker 进行调用。

调用流程-客户端源码分析

handler的调用链路为: InvokerInvocationHandler(MockClusterWrapper(FailoverCluster(directory)))

图解调用链

1、InvokerInvocationHandler.invoke

这个方法主要判断当前调用的远程方法,如果是tostring、hashcode、equals,就直接返回 否则,调用invoker.invoke,进入到 MockClusterWrapper.invok

    @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(new RpcInvocation(method, args)).recreate();
    }

2、MockClusterInvoker.invoke

Mock,在这里面有两个逻辑

  •    1. 是否客户端强制配置了mock调用,那么在这种场景中主要可以用来解决服务端还没开发好的时候 直接使用本地数据进行测试
  •    2. 是否出现了异常,如果出现异常则使用配置好的Mock类来实现服务的降级
 @Override
    public Result invoke(Invocation invocation) throws RpcException {
        Result result = null;

        //mock ->
        String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), MOCK_KEY, Boolean.FALSE.toString()).trim();
        if (value.length() == 0 || value.equalsIgnoreCase("false")) {
            // 不走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
            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);
                }
                result = doMockInvoke(invocation, e); //调用Mock类进行返回
            }
        }
        return result;
    }

3、AbstractClusterInvoker.invoke

下一个invoke,应该进入FailoverClusterInvoke,但是在这里它又用到了模版方法,所以直接进入到父类的invoke方法中

1. 绑定attachments,Dubbo中,可以通过 RpcContext 上的 setAttachment 和 getAttachment 在 服务消费方和提供方之间进行参数的隐式传递,所以这段代码中会去绑定attachments

RpcContext.getContext().setAttachment("index", "1")

2. 通过list获得invoker列表,这个列表基本可以猜测到是从directory里面获得的、但是这里面还实现 了服务路由的逻辑,简单来说就是先拿到invoker列表,然后通过router进行服务路由,筛选出符 合路由规则的服务提供者(暂时不细讲,属于另外一个逻辑)

3. initLoadBalance 初始化负载均衡机制

4. 执行doInvoke

 @Override
    public Result invoke(final Invocation invocation) throws RpcException {
        checkWhetherDestroyed();//检查 连接是否被销毁
        // attachments -> 隐式传参
        //RpcContext.getContext().setAttachment("key","value");
        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<T>> invokers = list(invocation);
        //invokers -> route决定了invokers返回多少的问题(tag->a(2), tag->b)
        /**
         * 通过在server端和client端都可以配置 loadbalance="random"  "roundrobin"
         *     获得url里面配置的负载均衡策略,如果没有,默认为random
         *     spi -> 通过自适应扩展点进行适配->得到真正意义上的实现
         *     容错 -> failover重试 -> 已经调用过失败的节点,如果下次重试,肯定不会再次调用。
         *   ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(DEFAULT_LOADBALANCE);
         */
        // 初始化负载均衡的机制 通过spi机制获取实例 默认 RandomLoadBalance
        //loadbalace ->RandomLoadBalance
        LoadBalance loadbalance = initLoadBalance(invokers, invocation);
         RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
       // 抽象方法,由子类实现
        return doInvoke(invocation, invokers, loadbalance);
    }

稍微总结一下就是 FailoverClusterInvoker 拿到 Directory 返回的 Invoker 列表,并且经过路由之后,它会让 LoadBalance 从 Invoker 列表中选择一个 Invoker

initLoadBalance

不用看这个代码,基本也能猜测到,会从url中获得当前的负载均衡算法,然后使用spi机制来获得负载 均衡的扩展点。然后返回一个具体的实现

 protected LoadBalance initLoadBalance(List<Invoker<T>> invokers, Invocation invocation) {
        if (CollectionUtils.isNotEmpty(invokers)) {
            return ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
                    .getMethodParameter(RpcUtils.getMethodName(invocation), LOADBALANCE_KEY, DEFAULT_LOADBALANCE));
        } else {
            return ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(DEFAULT_LOADBALANCE);
        }
    }

4、FailoverClusterInvoker.doInvoke

这段代码逻辑也很好理解,因为我们之前在讲Dubbo的时候说过容错机制,而failover是失败重试,所 以这里面应该会实现容错的逻辑

  1. 获得重试的次数,并且进行循环
  2. 获得目标服务,并且记录当前已经调用过的目标服务防止下次继续将请求发送过去
  3. 如果执行成功,则返回结果
  4. 如果出现异常,判断是否为业务异常,如果是则抛出,否则,进行下一次重试
   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, RETRIES_KEY, DEFAULT_RETRIES) + 1;
        if (len <= 0) {
            len = 1;
        }
        // retry loop.
        RpcException le = null; // 异常信息
        //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 {
                //invoker -> InvokerDelegate(ProtocolFilterWrapper(ListenerInvokerWrapper(DubboInvoker)
                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()) { // 业务异常,不进行重试
                    throw e;
                }
                le = e; //保存异常信息
            } catch (Throwable e) {
                le = new RpcException(e.getMessage(), e);
            } finally {
                providers.add(invoker.getUrl().getAddress());
            }
        }
      }
    

5、负载均衡 select() RandomLoadBalance

在调用invoker.invoke之前,会需要通过select选择一个合适的服务进行调用,而这个选择的过程其实 就是负载均衡的实现 所有负载均衡实现类均继承自 AbstractLoadBalance,该类实现了 LoadBalance 接口,并封装了一些 公共的逻辑。所以在分析负载均衡实现之前,先来看一下 AbstractLoadBalance 的逻辑。首先来看一 下负载均衡的入口方法 select,如下:

 @Override
    public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {
        if (CollectionUtils.isEmpty(invokers)) {
            return null;
        }
        if (invokers.size() == 1) { //目标服务只有一个1, 直接返回这一个
            return invokers.get(0);
        }
        //模板方法在子类中实现
        return doSelect(invokers, url, invocation);
    }

负载均衡的子类实现有四个,默认情况下是RandomLoadBalance。这里就不进行分析了。。

然后带着这些 Invoker 再进行一波 loadbalance 的挑选,得到一个 Invoker,我们默认使用的是 FailoverClusterInvoker,也就是失败自动切换的容错方式。

发起一个远程调用  invoker -> InvokerDelegate(ProtocolFilterWrapper(ListenerInvokerWrapper(DubboInvoker)))

Result result = invoker.invoke(invocation); //发起一个远程调用

发起调用的这个 invoke 又是调用抽象类中的 invoke 然后再调用子类的 doInvoker,抽象类中的方法很简单我就不展示了,影响不大,直接看子类 DubboInvoker 的 doInvoke 方法。

6、DubboInvoker.doInvoker

这里面看到一个很熟悉的东西,就是ExchangeClient,这个是客户端和服务端之间的连接 然后如果当前方法有返回值,也就是isOneway=false,则执行else逻辑,然后通过异步的形式进行通信

    @Override
    protected Result doInvoke(final Invocation invocation) throws Throwable {
        RpcInvocation inv = (RpcInvocation) invocation;
        final String methodName = RpcUtils.getMethodName(invocation);
        inv.setAttachment(PATH_KEY, getUrl().getPath());
        inv.setAttachment(VERSION_KEY, version);

        ExchangeClient currentClient; //初始化invoker的时候,构建的一个远程通信连接
        if (clients.length == 1) { //默认
            currentClient = clients[0];
        } else {
            //通过取模获得其中一个连接
            currentClient = clients[index.getAndIncrement() % clients.length];
        }
        try {
            //表示当前的方法是否存在返回值
            boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
            int timeout = getUrl().getMethodParameter(methodName, TIMEOUT_KEY, DEFAULT_TIMEOUT);
            if (isOneway) {//不存在返回值
                boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
                currentClient.send(inv, isSent);
                RpcContext.getContext().setFuture(null);
                return AsyncRpcResult.newDefaultAsyncResult(invocation);
            } else { //存在返回值
                //是否采用异步
                AsyncRpcResult asyncRpcResult = new AsyncRpcResult(inv);
                //timeout ->超时时间
                //currentClient -> ReferenceCountExhcangeClient(HeaderExchangeClient(HeaderExchangeChannel( ->request)
                CompletableFuture<Object> responseFuture = currentClient.request(inv, timeout);
                responseFuture.whenComplete((obj, t) -> {
                    if (t != null) {
                        asyncRpcResult.completeExceptionally(t);
                    } else {
                        asyncRpcResult.complete((AppResponse) obj);
                    }
                });
                RpcContext.getContext().setFuture(new FutureAdapter(asyncRpcResult));
                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);
        }
    }

调用的三种方式  分别是 oneway、异步、同步。

7、currentClient.request

currentClient还记得是一个什么对象吗? 它实际是一个ReferenceCountExchangeClient(HeaderExchangeClient())

所以它的调用链路是 ReferenceCountExchangeClient->HeaderExchangeClient->HeaderExchangeChannel->(request方 法)

最终,把构建好的RpcInvocation,组装到一个Request对象中进行传递。

  @Override
    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!");
        }
        // 组装一个request
        Request req = new Request();
        req.setVersion(Version.getProtocolVersion()); //
        req.setTwoWay(true);
        req.setData(request); //Invocation ->
        DefaultFuture future = DefaultFuture.newFuture(channel, req, timeout);
        try {
            channel.send(req);
        } catch (RemotingException e) {
            future.cancel();
            throw e;
        }
        return future;
    }

channel.send的调用链路 AbstractPeer.send ->AbstractClient.send->NettyChannel.send

通过NioSocketChannel把消息发送出去

ChannelFuture future = channel.writeAndFlush(message);

8、 DefaultFuture 

我们再来看一下 DefaultFuture 的内部,你有没有想过一个问题,因为是异步,那么这个 future 保存了之后,等响应回来了如何找到对应的 future 呢?

这里就揭秘了!就是利用一个唯一 ID。

 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);// 这里就是唯一id 和 DefaultFuture的关系
        CHANNELS.put(id, channel);
    }

可以看到 Request 会生成一个全局唯一 ID,然后 future 内部会将自己和 ID 存储到一个 ConcurrentHashMap。这个 ID 发送到服务端之后,服务端也会把这个 ID 返回来,这样通过这个 ID 再去ConcurrentHashMap 里面就可以找到对应的 future ,这样整个连接就正确且完整了!

我们再来看看最终接受到响应的代码,应该就很清晰了。

先看下一个响应的 message 的样子:

Response [id=14, version=null, status=20, event=false, error=null, result=RpcResult [result=Hello world, response from provider: 192.168.1.17:20881, exception=null]]

看到这个 ID 了吧,最终会调用 DefaultFuture#received的方法。

为了能让大家更加的清晰,我再画个图:

发起请求的调用链如下图所示:

 

处理请求响应的调用链如下图所示:

三、调用流程-服务端端源码分析

客户端把消息发送出去之后,服务端会收到消息,然后把执行的结果返回到客户端。

服务端这边接收消息的处理链路,也比较复杂,我们回到NettServer中创建io的过程

handler配置的是nettyServerHandler

Handler与Servlet中的filter很像,通过Handler可以完成通讯报文的解码编码、拦截指定的报文、统一 对日志错误进行处理、统一对请求进行计数、控制Handler执行与否

1、NettyServerHandler.channelRead()

服务端收到读的请求是,会进入这个方法。 接着通过handler.received来处理msg,这个handle的链路很长,比较复杂,我们需要逐步剖析

 @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
        try {
            handler.received(channel, msg); //接收到客户端的请求时候
        } finally {
            NettyChannel.removeChannelIfDisconnected(ctx.channel());
        }
    }

handler->MultiMessageHandler->HeartbeatHandler->AllChannelHandler->DecodeHandler- >HeaderExchangeHandler->最后进入这个方法->DubboProtocol$requestHandler(receive)

  • MultiMessageHandler: 复合消息处理
  • HeartbeatHandler:心跳消息处理,接收心跳并发送心跳响应
  • AllChannelHandler:业务线程转化处理器,把接收到的消息封装成ChannelEventRunnable可执行任 务给线程池处理
  • DecodeHandler:业务解码处理器

服务端处理链路

HeaderExchangeHandler.received

交互层请求响应处理,有三种处理方式

  1. handlerRequest,双向请求
  2. handler.received 单向请求
  3. handleResponse 响应消息

ExchangeHandler.reply

接着进入到ExchangeHandler.reply这个方法中

  1. 把message转化为Invocation
  2. 调用getInvoker获得一个Invoker对象
  3. 然后通过 Result result = invoker.invoke(inv); 进行调用
 @Override
        public CompletableFuture<Object> reply(ExchangeChannel channel, Object message) throws RemotingException {
           Invocation inv = (Invocation) message;
            Invoker<?> invoker = getInvoker(channel, inv);
          
            RpcContext rpcContext = RpcContext.getContext();
            rpcContext.setRemoteAddress(channel.getRemoteAddress());
            Result result = invoker.invoke(inv);

            if (result instanceof AsyncRpcResult) {
                return ((AsyncRpcResult) result).getResultFuture().thenApply(r -> (Object) r);

            } else {
                return CompletableFuture.completedFuture(result);
            }
        }

getInvoker

Invoker invoker = getInvoker(channel, inv);

这里面是获得一个invoker的实现

DubboExporter exporter = (DubboExporter) exporterMap.get(serviceKey);

关键就是那个 serviceKey, 还记得之前服务暴露将invoker 封装成 exporter 之后再构建了一个 serviceKey将其和 exporter 存入了 exporterMap 中吧,这 map 这个时候就起作用了!

得到结果invoker.invoke(inv);

invoker=ProtocolFilterWrapper(InvokerDelegate(DelegateProviderMetaDataInvoker(AbstractProxy Invoker)))

最后一定会进入到这个代码里面 AbstractProxyInvoker

在AbstractProxyInvoker里面,doInvoker本质上调用的是wrapper.invokeMethod()。

   @Override
    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
        //会创建一个动态代理,核心的方法 会生成 三个方法 setPropertyValue getPropertyValue invokeMethod 三个方法
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);

        return new AbstractProxyInvoker<T>(proxy, type, url) {

            @Override
            protected Object doInvoke(T proxy, String methodName,
                                      Class<?>[] parameterTypes,
                                      Object[] arguments) throws Throwable {
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }

而Wrapper是一个动态代理类,它的定义是这样的, 最终调用w.sayHello()方法进行处理。

找到 invoker 最终调用实现类具体的方法再返回响应整个流程就完结了,我再补充一下之前的图。

 

总结

今天的调用过程我再总结一遍应该差不多了。

首先客户端调用接口的某个方法,实际调用的是代理类,代理类会通过 cluster 从 directory 中获取一堆 invokers(如果有一堆的话),然后进行 router 的过滤(其中看配置也会添加 mockInvoker 用于服务降级),然后再通过 SPI 得到 loadBalance 进行一波负载均衡。

这里要强调一下默认的 cluster 是 FailoverCluster ,会进行容错重试处理,这个日后再详细分析。

现在我们已经得到要调用的远程服务对应的 invoker 了,此时根据具体的协议构造请求头,然后将参数根据具体的序列化协议序列化之后构造塞入请求体中,再通过 NettyClient 发起远程调用。

服务端 NettyServer 收到请求之后,根据协议得到信息并且反序列化成对象,再按照派发策略派发消息,默认是 All,也就是所有消息都派发到业务线程池。

业务线程会根据消息类型判断然后得到 serviceKey 从之前服务暴露生成的 exporterMap 中得到对应的 Invoker ,然后调用真实的实现类。

最终将结果返回,因为请求和响应都有一个统一的 ID, 客户端根据响应的 ID 找到存储起来的 Future, 然后塞入响应再唤醒等待 future 的线程,完成一次远程调用全过程。

当然其实隐藏了很多设计模式在其中,比如责任链、装饰器等等,没有特意挑开来说,源码中太常见了,基本上无处不在。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值