【Dubbo 源码解析】06_Dubbo 服务调用

 

Dubbo 服务调用

 

根据上图,能够看出,服务调用过程为:html

  1. Consumer 端的 Proxy 调用 Cluster 层选择集群中的某一个 Invoker(负载均衡)apache

  2. Invoker 最终会调用 Protocol 层进行 RPC 通信(netty,tcp 长链接),将服务调用信息和配置信息进行传递c#

  3. Provider 端 Protocol 层接收到服务调用信息后,最终会调用真实的服务实现负载均衡

 

Consumer 端调用过程

经过前面 Dubbo 服务发现&引用 的学习,咱们知道,Consumer 端的调用过程大致以下:异步

  1. 执行 FailoverClusterInvoker#invoke(Invocation invocation) (负载均衡。当有多个 provider 时走这一步,没有的话,就跳过)tcp

  2. 执行 Filter#invoke(Invoker<?> invoker, Invocation invocation) (全部 group="provider" 的 Filter)ide

  3. 执行 DubboInvoker#invoke(Invocation inv)学习

因此,最终的通信过程是由 DubboInvoker 来完成的:编码

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(通常状况下只有一个)
 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); RpcContext.getContext().setFuture(null); return new RpcResult(); } else if (isAsync) { // dubbo 异步调用
            ResponseFuture future = currentClient.request(inv, timeout); RpcContext.getContext().setFuture(new FutureAdapter<Object>(future)); return new RpcResult(); } 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); } }

 

阅读源码能够发现,获取 Rpc 调用结果的方法是: ExchangeClient#request(inv, timeout).get(), 最终会调用 NettyChannel#send(Object message, false) 去写消息。而消息的编解码是经过 com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec 来处理的。spa

// 对 Request 消息进行编码(DubboCodec.class)
protected void encodeRequestData(Channel channel, ObjectOutput out, Object data) throws IOException { RpcInvocation inv = (RpcInvocation) data; out.writeUTF(inv.getAttachment(Constants.DUBBO_VERSION_KEY, DUBBO_VERSION)); out.writeUTF(inv.getAttachment(Constants.PATH_KEY)); out.writeUTF(inv.getAttachment(Constants.VERSION_KEY)); out.writeUTF(inv.getMethodName()); // 写出调用方法的名称
    out.writeUTF(ReflectUtils.getDesc(inv.getParameterTypes())); // 写出调用方法的参数
    Object[] args = inv.getArguments(); if (args != null) for (int i = 0; i < args.length; i++) { out.writeObject(encodeInvocationArgument(channel, inv, i)); // 写出调用的实参
 } out.writeObject(inv.getAttachments()); // 写出 attachments
}

 

 

Provider 端调用过程

Provider 端接收到 Consumer 端的消息后,会经过 DubboCodec#decodeBody(Channel channel, InputStream is, byte[] header)处理,将消息转化为 RpcInvocation。最终经过预先设置好的 netty 的 handler 来将 RpcInvocation 转化为 Invoker 的调用。

经过 Dubbo 服务发现&引用 的学习,咱们知道,这个 handler 最终会调用 DubboProtocol#requestHandler 来处理,最终将 Invocation 转化为 Invoker 的调用。

调用完成后,返回结果经过DubboCodec#encodeResponseData(Channel channel, ObjectOutput out, Object data)进行编码,最终写回到 Consumer 端:

// 对返回结果进行编码
protected void encodeResponseData(Channel channel, ObjectOutput out, Object data) throws IOException { Result result = (Result) data; Throwable th = result.getException(); if (th == null) { Object ret = result.getValue(); if (ret == null) { out.writeByte(RESPONSE_NULL_VALUE); } else { out.writeByte(RESPONSE_VALUE); out.writeObject(ret); } } else { out.writeByte(RESPONSE_WITH_EXCEPTION); out.writeObject(th); } }

 

 

Consumer 端接收调用返回

Conmuser 端接收到调用的返回数据后,会由 DubboCodec#decodeBody(Channel channel, InputStream is, byte[] header)将数据转化成 RpcResult。

ExchangeClient#request(inv, timeout).get() 最终会调用 DefaultFuture#get() 来获取 Response 中的返回数据。DefaultFuture#get() 使用了同步阻塞的方式来等待 provider 端的返回数据。

 

 

官方如是说:

满眼都是 Invoker

因为 Invoker 是 Dubbo 领域模型中很是重要的一个概念,不少设计思路都是向它靠拢。这就使得 Invoker渗透在整个实现代码里,对于刚开始接触 Dubbo 的人,确实容易给搞混了。 下面咱们用一个精简的图来讲明最重要的两种 Invoker:服务提供 Invoker 和服务消费 Invoker

服务提供 Invoker:DubboInvoker(默认)、HessianRpcInvoker等(视协议而定)

服务消费 Invoker:AbstractProxyInvoker