一、简介
dubbo的服务调用主要包括几个大的步骤
1.发送请求
2.编解码
3.服务降级
4.过滤器链处理
5.序列化
6.线程派发以及响应请求
Dubbo 服务调用过程
dubbo中Exchange 层为框架引入 Request 和 Response 语义
二、服务消费者发起调用
dubbo服务调用支持的方式
同步调用(默认)(本篇讲述这个)
异步调用
1.有返回值
2.无返回值(不关心结果。直接返回一个空的 RpcResult)
触发的调用链
能够看到先是通过ReferenceAnnotationBeanPostProcessor$ReferenceBeanInvocationHandler.invoke-------->反射调用-------->DelegatingMethodAccessorImpl.invoke-------->NativeMethodAccessorImpl.invoke-------->代理类proxy0.sayHello--------> InvokerInvocationHandler.invoke
代理类的代码
Dubbo 默认使用 Javassist 框架为服务接口生成动态代理类,因此我们需要先将代理类进行反编译才能看到源码
反编译后看一下代理类的代码
(反编译的方式看这章:dubbo源码分析之服务调用方refer(服务引用、创建invoker、创建代理、查看动态生成的.class文件))
package com.alibaba.dubbo.common.bytecode;
import com.alibaba.dubbo.common.bytecode.ClassGenerator.DC;
import com.alibaba.dubbo.rpc.service.EchoService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import org.apache.dubbo.samples.api.client.HelloService;
public class proxy0 implements DC, HelloService, EchoService {
// 方法数组
public static Method[] methods;
private InvocationHandler handler;
public proxy0(InvocationHandler var1) {
this.handler = var1;
}
public proxy0() {
}
public String sayHello(String var1) {
// 将参数存储到 Object 数组中
Object[] var2 = new Object[]{var1};
// 调用 InvocationHandler 实现类的 invoke 方法得到调用结果
Object var3 = this.handler.invoke(this, methods[0], var2);
// 返回调用结果
return (String)var3;
}
/** 回声测试方法 */
public Object $echo(Object var1) {
Object[] var2 = new Object[]{var1};
Object var3 = this.handler.invoke(this, methods[1], var2);
return (Object)var3;
}
主要做了几件事:
1.将运行时参数存储到数组中
2.调用 InvocationHandler 接口实现类的 invoke 方法,得到调用结果
3.将结果转型并返回给调用方。
跟进去看一下这个invoke方法
InvokerInvocationHandler.invoke
@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();
}
主要做了几件事:
1.如果调用的方法是属于Object的,比如wait/notify,那么直接调用AnnotationHelloServiceConsumer类的该方法
2.如果调用的方法是toString hashCode equals,那么调用invoker对应的该方法
此处的invoker是
3.如果调用的是服务提供方的方法,则invoker.invoke进行RPC远程调用
MockClusterInvoker.invoke
public Result invoke(Invocation invocation) throws RpcException {
Result result = null;
// 获取 mock 配置值
String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim();
if (value.length() == 0 || value.equalsIgnoreCase("false")) {
//debug的时候走的这里
// 无 mock 逻辑,直接调用其他 Invoker 对象的 invoke 方法,
// 比如 FailoverClusterInvoker
//no mock
result = this.invoker.invoke(invocation);
} else if (value.startsWith("force")) {
if (logger.isWarnEnabled()) {
logger.info("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + directory.getUrl());
}
// force:xxx 直接执行 mock 逻辑,不发起远程调用
//force:direct mock
result = doMockInvoke(invocation, null);
} else {
// fail:xxx 表示消费方对调用服务失败后,再执行 mock 逻辑,不抛出异常
//fail-mock
try {
// 调用其他 Invoker 对象的 invoke 方法
result = this.invoker.invoke(invocation);
} catch (RpcException e) {
if (e.isBiz()) {
throw e;
} else {
if (logger.isWarnEnabled()) {
logger.warn("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + directory.getUrl(), e);
}
// 调用失败,执行 mock 逻辑
result = doMockInvoke(invocation, e);
}
}
}
return result;
}
主要做了几件事:
1.获取 mock 配置值
2.如果无 mock 逻辑,直接调用其他 Invoker 对象的 invoke 方法,比如 FailoverClusterInvoker.invoke
3.如果mock配置是 force:xxx 直接调用doMockInvoke方法执行 mock 逻辑,不发起远程调用
4. 如果Mock配置是 fail:xxx 表示消费方使用Invoker.invoke调用服务失败后,再使用doMockInvoke执行 mock 逻辑,不抛出异常
其中 doMockInvoke跟服务降级相关,后面再专门写一篇说这个
AbstractClusterInvoker.invoke
@Override
public Result invoke(final Invocation invocation) throws RpcException {
checkWhetherDestroyed();
LoadBalance loadbalance = null;
//将RpcInvocation与attachments绑定
// binding attachments into invocation.
Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
if (contextAttachments != null && contextAttachments.size() != 0) {
((RpcInvocation) invocation).addAttachments(contextAttachments);
}
//调用Directory的list方法,得到符合路由条件的invoker
List<Invoker<T>> invokers = list(invocation);
//得到负载均衡器LoadBalance
if (invokers != null && !invokers.isEmpty()) {
loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
.getMethodParameter(RpcUtils.getMethodName(invocation), Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE));
}
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
//执行调用
return doInvoke(invocation, invokers, loadbalance);
}
做了几件事:
1.将RpcInvocation与attachments绑定
执行后
2.调用Directory的list方法,得到符合路由条件的invoker
3.得到负载均衡器LoadBalance
debug的时候得到的是
估计默认就是RandomLoadBalance
4.RpcUtils.attachInvocationIdIfAsync方法
执行后
看起来没什么区别
4.执行调用failoverClusterInvoker.doInvoke
FailoverClusterInvoker.doInvoke
这个类的这个方法上一章分析过了
这个方法里面再往后面调用,的调用栈是:
FailoverClusterInvoker.doInvoke-------->InvokerWrapper.invoke-------->ListenerInvoker.invoke-------->ProtocolFilterWrapper.invoke-------->ConsumerContextFilter.invoke-------->ProtocolFilterWrapper.invoke-------->FutureFilter.invoke-------->ProtocolFilter.invoke-------->MonitorFilter.invoke-------->AbstractInvoker.invoke
那下面我们来看
AbstractInvoker.invoke
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 " + this + " on consumer " + NetUtils.getLocalHost() + " is destroyed, "
+ ", dubbo version is " + Version.getVersion() + ", this invoker should not be used any longer");
}
RpcInvocation invocation = (RpcInvocation) inv;
// 设置 Invoker
invocation.setInvoker(this);
if (attachment != null && attachment.size() > 0) {
// 设置 attachment
invocation.addAttachmentsIfAbsent(attachment);
}
Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
if (contextAttachments != null && contextAttachments.size() != 0) {
/**
* 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).
*/
// 添加 contextAttachments 到 RpcInvocation#attachment 变量中
invocation.addAttachments(contextAttachments);
}
if (getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)) {
// 设置异步信息到 RpcInvocation#attachment 中
invocation.setAttachment(Constants.ASYNC_KEY, Boolean.TRUE.toString());
}
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
try {
// 抽象方法,由子类实现
return doInvoke(invocation);
} catch (InvocationTargetException e) { // biz exception
Throwable te = e.getTargetException();
if (te == null) {
return new RpcResult(e);
} else {
if (te instanceof RpcException) {
((RpcException) te).setCode(RpcException.BIZ_EXCEPTION);
}
return new RpcResult(te);
}
} catch (RpcException e) {
if (e.isBiz()) {
return new RpcResult(e);
} else {
throw e;
}
} catch (Throwable e) {
return new RpcResult(e);
}
}
做了几件事:
1.添加信息到 RpcInvocation#attachment 变量中
添加完后
2.调用 子类doInvoke 执行后续的调用。debugg的时候就是调用的 DubboInvoker 类的doInvoke
DubboInvoker.doInvoke
该方法是 Dubbo 对同步和异步调用的处理逻辑
同步调用模式下,由框架自身调用 ResponseFuture 的 get 方法。
异步调用模式下,则由用户调用该方法。
protected Result doInvoke(final Invocation invocation) throws Throwable {
RpcInvocation inv = (RpcInvocation) invocation;
final String methodName = RpcUtils.getMethodName(invocation);
// 设置 path 和 version 到 attachment 中
inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());
inv.setAttachment(Constants.VERSION_KEY, version);
ExchangeClient currentClient;
if (clients.length == 1) {
// 从 clients 数组中获取 ExchangeClient
currentClient = clients[0];
} else {
currentClient = clients[index.getAndIncrement() % clients.length];
}
try {
// 获取异步配置
boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
// isOneway 为 true,表示“单向”通信
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);
// 设置上下文中的 future 字段为 null
RpcContext.getContext().setFuture(null);
// 返回一个空的 RpcResult
return new RpcResult();
// 异步有返回值
} else if (isAsync) {
// 发送请求,并得到一个 ResponseFuture 实例
ResponseFuture future = currentClient.request(inv, timeout);
// 设置 future 到上下文中
RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
// 暂时返回一个空结果
return new RpcResult();
// 同步调用
} else {
RpcContext.getContext().setFuture(null);
// 发送请求,得到一个 ResponseFuture 实例,并调用该实例的 get 方法进行等待
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);
}
}
主要做了几件事:
1.设置attachment
2.得到Exchange(debug的时候这个Exchange是ReferenceCountExchangeClient,这个底层发送的逻辑后面专门写一篇来说),调用它的send或者request方法进行远程调用
它是做底层操作的,发起远程调用就是它负责
3.根据标识执行对应的调用方式
比如是,同步调用、异步有返回值调用、异步无返回值调用
默认是同步调用,debug的时候也是走的同步调用
而debug的时候这个currentClient.request得到的值是DefaultFuture
DefaultFuture
public class DefaultFuture implements ResponseFuture {
private static final Logger logger = LoggerFactory.getLogger(DefaultFuture.class);
private static final Map<Long, Channel> CHANNELS = new ConcurrentHashMap<Long, Channel>();
private static final Map<Long, DefaultFuture> FUTURES = new ConcurrentHashMap<Long, DefaultFuture>();
static {
Thread th = new Thread(new RemotingInvocationTimeoutScan(), "DubboResponseTimeoutScanTimer");
th.setDaemon(true);
th.start();
}
// invoke id.
private final long id;
private final Channel channel;
private final Request request;
private final int timeout;
private final Lock lock = new ReentrantLock();
private final Condition done = lock.newCondition();
private final long start = System.currentTimeMillis();
private volatile long sent;
private volatile Response response;
private volatile ResponseCallback callback;
public DefaultFuture(Channel channel, Request request, int timeout) {
this.channel = channel;
this.request = request;
// 获取请求 id,这个 id 很重要,后面还会见到
this.id = request.getId();
this.timeout = timeout > 0 ? timeout : channel.getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
// put into waiting map.
// 存储 <requestId, DefaultFuture> 映射关系到 FUTURES 中
FUTURES.put(id, this);
CHANNELS.put(id, channel);
}
@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);
// 如果调用结果成功返回,或等待超时,此时跳出 while 循环,执行后续的逻辑
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();
}
@Override
public boolean isDone() {
// 通过检测 response 字段为空与否,判断是否收到了调用结果
return response != null;
}
private Object returnFromResponse() throws RemotingException {
Response res = response;
if (res == null) {
throw new IllegalStateException("response cannot be null");
}
// 如果调用结果的状态为 Response.OK,则表示调用过程正常,服务提供方成功返回了调用结果
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());
}
}
主要做了1件事
当服务消费者还未接收到调用结果时,用户线程调用 get 方法会被阻塞住。
同步调用模式下,框架获得 DefaultFuture 对象后,会立即调用 get 方法进行等待。
而异步模式下则是将该对象封装到 FutureAdapter 实例中,并将 FutureAdapter 实例设置到 RpcContext 中,供用户使用。
FutureAdapter 是一个适配器,用于将 Dubbo 中的 ResponseFuture 与 JDK 中的 Future 进行适配。
这样当用户线程调用 Future 的 get 方法时,经过 FutureAdapter 适配,最终会调用 ResponseFuture 实现类对象的 get 方法,也就是 DefaultFuture 的 get 方法。
三、服务调用方发送请求
dubbo中Exchange 层为框架引入 Request 和 Response 语义
ReferenceCountExchangeClient类
/**
* dubbo protocol support class.
*/
@SuppressWarnings("deprecation")
final class ReferenceCountExchangeClient implements ExchangeClient {
private final URL url;
private final AtomicInteger refenceCount = new AtomicInteger(0);
// private final ExchangeHandler handler;
private final ConcurrentMap<String, LazyConnectExchangeClient> ghostClientMap;
private ExchangeClient client;
public ReferenceCountExchangeClient(ExchangeClient client, ConcurrentMap<String, LazyConnectExchangeClient> ghostClientMap) {
this.client = client;
refenceCount.incrementAndGet();
this.url = client.getUrl();
if (ghostClientMap == null) {
throw new IllegalStateException("ghostClientMap can not be null, url: " + url);
}
this.ghostClientMap = ghostClientMap;
}
@Override
public ResponseFuture request(Object request) throws RemotingException {
return client.request(request);
}
@Override
public ResponseFuture request(Object request, int timeout) throws RemotingException {
return client.request(request, timeout);
}
@Override
public void send(Object message) throws RemotingException {
client.send(message);
}
@Override
public void send(Object message, boolean sent) throws RemotingException {
client.send(message, sent);
}
public void incrementAndGetCount() {
refenceCount.incrementAndGet();
}
}
ReferenceCountExchangeClient 内部仅实现了一个引用计数的功能,其他方法并无复杂逻辑,均是直接调用被装饰对象的相关方法。
每当该对象被引用一次 referenceCount 都会进行自增
每当 close 方法被调用时,referenceCount 进行自减。
debug的时候是该类的request方法被调用,该方法里面就是直接调用被装饰对象的request方法,这里的被装饰对象HeaderExchangeClient
HeaderExchangeClient.request
HeaderExchangeClient 中很多方法只有一行代码,即调用 HeaderExchangeChannel 对象的同签名方法。
HeaderExchangeClient 的用处是封装了一些关于心跳检测的逻辑。
@Override
public ResponseFuture request(Object request, int timeout) throws RemotingException {
return channel.request(request, timeout);
}
HeaderExchangeChannel.request
@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 req = new Request();
req.setVersion(Version.getProtocolVersion());
req.setTwoWay(true);
req.setData(request);
DefaultFuture future = new DefaultFuture(channel, req, timeout);
try {
channel.send(req);
} catch (RemotingException e) {
future.cancel();
throw e;
}
return future;
}
做了几件事
1.首先定义了一个 Request 对象
2.然后再将该对象传给 NettyClient 的 send 方法,进行后续的调用。
这里的channel.send方法,看一下channel是什么
注意:NettyClient 中并未实现 send 方法,该方法继承自父类 AbstractPeer。
AbstractPeer.send
@Override
public void send(Object message) throws RemotingException {
send(message, url.getParameter(Constants.SENT_KEY, false));
}
调用子类的send方法,这里的子类是AbstractClient
AbstractClient.send
@Override
public void send(Object message, boolean sent) throws RemotingException {
if (send_reconnect && !isConnected()) {
//第一次debug的时候没有走这里
connect();
}
// 获取 Channel,getChannel 是一个抽象方法,具体由子类实现
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);
}
Dubbo 使用 Netty 作为底层的通信框架。跟一下这个getChannel方法
NettyClient.getChannel
@Override
protected com.alibaba.dubbo.remoting.Channel getChannel() {
Channel c = channel;
if (c == null || !c.isActive())
return null;
// 获取一个 NettyChannel 类型对象
return NettyChannel.getOrAddChannel(c, getUrl(), this);
}
看一下这个channel是什么
NettyChannel.getOrAddChannel
static NettyChannel getOrAddChannel(Channel ch, URL url, ChannelHandler handler) {
if (ch == null) {
return null;
}
// 尝试从集合中获取 NettyChannel 实例
NettyChannel ret = channelMap.get(ch);
if (ret == null) {
// 如果 ret = null,则创建一个新的 NettyChannel 实例
NettyChannel nettyChannel = new NettyChannel(ch, url, handler);
if (ch.isActive()) {
// 将 <Channel, NettyChannel> 键值对存入 channelMap 集合中
ret = channelMap.putIfAbsent(ch, nettyChannel);
}
if (ret == null) {
ret = nettyChannel;
}
}
return ret;
}
NettyChannel.send
@Override
public void send(Object message, boolean sent) throws RemotingException {
super.send(message, sent);
boolean success = true;
int timeout = 0;
try {
// 发送消息(包含请求和响应消息)
ChannelFuture future = channel.writeAndFlush(message);
// sent 的值源于 <dubbo:method sent="true/false" /> 中 sent 的配置值,有两种配置值:
// 1. true: 等待消息发出,消息发送失败将抛出异常
// 2. false: 不等待消息发出,将消息放入 IO 队列,即刻返回
// 默认情况下 sent = false;
if (sent) {
timeout = getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.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);
}
// 若 success 为 false,这里抛出异常
if (!success) {
throw new RemotingException(this, "Failed to send message " + message + " to " + getRemoteAddress()
+ "in timeout(" + timeout + "ms) limit");
}
}
这个channel.writeAndFlush(message);发送请求,再跟进去就进到netty中了。
在 Netty 中,出站数据在发出之前还需要进行编码操作