1. 前言
Dubbo 服务调用过程比较复杂,包含众多步骤,比如发送请求、编解码、服务降级、过滤器链处理、序列化、线程派发以及响应请求等步骤。限于篇幅原因,本篇文章无法对所有的步骤一一进行分析。后续挖坑再说吧。本篇文章将会重点分析请求的发送与接收、线程派发以及响应的发送与接收等过程。
2. 源码分析
先了解下 Dubbo 服务调用过程(图片来自官方文档)

首先服务消费者通过代理对象 Proxy 发起远程调用,接着通过网络客户端 Client 将编码后的请求发送给服务提供方的网络层上,也就是 Server。Server 在收到请求后,首先要做的事情是对数据包进行解码。然后将解码后的请求发送至分发器 Dispatcher,再由分发器将请求派发到指定的线程池上,最后由线程池调用具体的服务。这就是一个远程调用请求的发送与接收过程。至于响应的发送与接收过程,这张图中没有表现出来。
2.1 服务调用方式
Dubbo 支持同步和异步两种调用方式,其中异步调用还可细分为“有返回值”的异步调用和“无返回值”的异步调用。所谓“无返回值”异步调用是指服务消费方只管调用,但不关心调用结果,此时 Dubbo 会直接返回一个空的 RpcResult。Dubbo 默认使用同步调用方式。
2.1.1 异步调用案例
当有返回值异步和无返回值异步同时存在,无返回值异步优先:
-
有返回值异步调用
修改配置,将参数
async设置为 true<dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService"> <dubbo:method name="sayHello" async="true" /> </dubbo:reference>代码使用如下
String hello = demoService.sayHello("world");// 返回值为null,要注意 Future<String> future = RpcContext.getContext().getFuture(); ... // 业务线程可以开始做其他事情 result = future.get(); -
无返回值异步调用
修改配置,将参数
return设置为 false<dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService"> <dubbo:method name="sayHello" return="false" /> </dubbo:reference>代码使用
String hello = demoService.sayHello("world");// 返回值为null,要注意 Future<String> future = RpcContext.getContext().getFuture();// future 为 null
下面,我们开始进入源码分析。
2.1.2 InvokerInvocationHandler
当我们通过Spring注入服务接口时,实际上注入的是服务接口的实现类,这个实现类由Dubbo框架生成。请看 服务引用#创建代理对象
package org.apache.dubbo.common.bytecode;
public class proxy0 implements org.apache.dubbo.demo.DemoService {
public static java.lang.reflect.Method[] methods;
private java.lang.reflect.InvocationHandler handler;
public proxy0() {
}
public proxy0(java.lang.reflect.InvocationHandler arg0) {
handler = 1;
}
public java.lang.String sayHello(java.lang.String arg0) {
Object[] args = new Object[1];
args[0] = (w) $1;
Object ret = handler.invoke(this, methods[0], args);
return (java.lang.String) ret;
}
}
也就是调用 demoService.sayHello 时,实际上是调用 handler.invoke ,而这个 handler 就是 InvokerInvocationHandler
public class InvokerInvocationHandler implements InvocationHandler {
private final Invoker<?> invoker;
public InvokerInvocationHandler(Invoker<?> handler) {
this.invoker = handler;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes();
// 拦截定义在 Object 类中的方法(未被子类重写),比如 wait/notify
if (method.getDeclaringClass() == Object.class) {
return method.invoke(invoker, args);
}
// 如果 toString、hashCode 和 equals 等方法被子类重写了,这里也直接调用
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]);
}
// 将 method 和 args 封装到 RpcInvocation 中,并执行后续的调用
return invoker.invoke(new RpcInvocation(method, args)).recreate();
}
}
invoke 方法判断如果是 java 内置的一下方法,则直接调用,不走 dubbo 的逻辑。所以我们关注的是 invoker.invoke() 。类变量 invoker 实际上是 FailoverClusterInvoker , 但是又被 MockClusterInvoker 包装了一层。这个 FailoverClusterInvoker 是由 FailoverCluster 生成的,请看 服务引用#远程引用 。而 MockClusterInvoker 是由 MockClusterWrapper 生成,其基于Dubbo的SPI机制,将 FailoverCluster 又包装了一遍。 MockClusterInvoker 内部封装了服务降级逻辑。以后再开坑聊。
我们在 Dubbo集群 文章中讲过 FailoverClusterInvoker ,所以直接快进到 DubboInvoker#doInvoke() 方法。此时是不是一脸懵逼,为啥从 FailoverClusterInvoker 一下子就到了 DubboInvoker ,我们先来看看调用栈

我们把视角拉回 FailoverClusterInvoker#doInvoke ,看看通过负载均衡选出的 invoker

从图片可以看到,最外层的invoker是一个内部类,是 服务目录通过订阅注册中心 生成的
invoker = new InvokerDelegate<T>(protocol.refer(serviceType, url), url, providerUrl);
而 protocol 实际是 DubboProtocol ,所以 protocol.refer(serviceType, url) 生成的是 DubboInvoker ,至于为啥调用链这么长,是因为 ProtocolFilterWrapper ,这个类增加了对Dubbo过滤器的支持。这是一个 protocol 的包装类,它包装了DubboProtocol#refer() ,我们取看看 ProtocolFilterWrapper 的源码
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
return protocol.refer(type, url);
}
// 创建invoker链条
return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
}
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;
// 对invoker进行封装,责任链模式
last = new Invoker<T>() {
......
@Override
public Result invoke(Invocation invocation) throws RpcException {
return filter.invoke(next, invocation);
}
};
}
}
return last;
}
buildInvokerChain 方法将 invoker 转换成责任链的形式,获取的 filters 为 {ConsumerContextFilter,FutureFilter,MonitorFilter},和图片中的调用栈就对应上了。
那么还剩下 ListenerInvokerWrapper ,这是一个 Invoker 包装类,由 ProtocolListenerWrapper 生成。
public class ProtocolListenerWrapper implements Protocol {
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
return protocol.refer(type, url);
}
// 封装了Invoker监听器
return new ListenerInvokerWrapper<T>(protocol.refer(type, url),
Collections.unmodifiableList(
ExtensionLoader.getExtensionLoader(InvokerListener.class)
.getActivateExtension(url, Constants.INVOKER_LISTENER_KEY)));
}
}
public class ListenerInvokerWrapper<T> implements Invoker<T> {
public ListenerInvokerWrapper(Invoker<T> invoker, List<InvokerListener> listeners) {
if (invoker == null) {
throw new IllegalArgumentException("invoker == null");
}
this.invoker = invoker;
this.listeners = li

本文深入分析了Dubbo服务调用的全过程,包括服务消费者如何通过Proxy发起调用,请求如何经过网络层到达服务提供方,服务提供方如何接收并处理请求,以及调用结果如何返回给消费者。详细探讨了同步和异步调用方式,特别是异步调用的实现机制。此外,还讲解了线程派发模型和服务调用路径,揭示了Dubbo服务调用的内部机制。
最低0.47元/天 解锁文章
796

被折叠的 条评论
为什么被折叠?



