前言

dubbo 是一个rpc框架,核心肯定是网络的发送和处理,这节我们将聊聊服务是怎么一层层最后通过netty4 发送到服务端的。

分析

上节我们已经分析过,业务代码对bean的调用是通过对javassist生成的Proxy$对象的调用,最后到了 InvokerInvocationHandler#invoke()。

@Override
public Object invoke(Object proxy, Method method, Object[] args){
String methodName = method.getName();
Class>[] parameterTypes = method.getParameterTypes();
if (method.getDeclaringClass() == Object.class) {
return method.invoke(invoker, args);
}
//....
return invoker.invoke(new RpcInvocation(method, args)).recreate();
}

复制代码

而invoker 对象我们之前分析过他最后的结果为 MockClusterInvoker(FailoverClusterInvoker(directory(invokers(RegistryDirectory$InvokerDelegate(ListenerInvokerWrapper(CallbackRegistrationInvoker(Filters(AsyncToSyncInvoker(DubboInvoker(clients[])))))))))) 我们一个个开始分析深入。
Mock
MockClusterInvoker 的作用是作为如果调用结果出错,自行自定义逻辑,有3种用法之前介绍过,我们看看实现
//org.....support.wrapper.MockClusterInvoker#invoke
public Result invoke(Invocation invocation) throws RpcException{
Result result = null;
String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), MOCK_KEY, Boolean.FALSE.toString()).trim();
if (value.length() == 0 || value.equalsIgnoreCase("false")) {//1
//no mock
result = this.invoker.invoke(invocation);
} else if (value.startsWith("force")) {//2
result = doMockInvoke(invocation, null);
} else {
try {
result = this.invoker.invoke(invocation);
} catch (RpcException e) {
if (e.isBiz()) { throw e; }
result = doMockInvoke(invocation, e);//3
}
}
return result;
}

复制代码

注释1是没有配置mock,注释2是配置强制走mock(当上游出问题,目前解决不了,下游可以这样配置),注释3当调用失败之后执行mock逻辑,返回mock执行后的结果,一般mock可以返回静态数据,我之前用过是去oss中提取提前存好的一些数据,优点类似降级。

失败重试(Failover)

FailoverClusterInvoker 是集群策略,调用第三方失败之后,可以选择重试n次。

//org.apache.dubbo.rpc.cluster.support.FailoverClusterInvoker#doInvoke
public Result doInvoke(Invocation invocation, final List> invokers, LoadBalance loadbalance) throws RpcException{
List> copyInvokers = invokers;
String methodName = RpcUtils.getMethodName(invocation);
int len = getUrl().getMethodParameter(methodName, RETRIES_KEY, DEFAULT_RETRIES) + 1;//1
RpcException le = null;
List> invoked = new ArrayList>(copyInvokers.size());
Set providers = new HashSet(len);
for (int i = 0; i < len; i++) {
Invoker invoker = select(loadbalance, invocation, copyInvokers, invoked);
try {
Result result = invoker.invoke(invocation);//2
return result;
} catch (RpcException e) {
if (e.isBiz()) { throw e; }
le = e;
} catch (Throwable e) {
le = new RpcException(e.getMessage(), e);
}
}
throw new RpcException(le.getCode(), "Failed to invoke the method);//3 }复制代码

代码比较简单,注释1 从配置retries=x 中获取配置的重试次数默认重试2次,注释2发起远程调用,注释3 如果重试调用还是失败,则抛错。我这里把对invoker的选择屏幕了,dubbo会将执行过的invoker 剔除,避免大概率也是执行失败。当然dubbo提供的还有FailfastClusterInvoker(快速失败),BroadcastClusterInvoker(广播),FailsafeClusterInvoker(安全失败)等等。

监听器(CallbackRegistrationInvoker)

CallbackRegistrationInvoker 主要的功能是为了在执行完invoker之后进行回调处理,回调在dubbo中是通过继承ListenableFilter过滤器实现的。

//org.xx.protocol.ProtocolFilterWrapper.CallbackRegistrationInvoker#invoke
public Result invoke(Invocation invocation) throws RpcException{
Result asyncResult = filterInvoker.invoke(invocation);
asyncResult = asyncResult.whenCompleteWithContext((r, t) -> {
for (int i = filters.size() - 1; i >= 0; i--) {//1
Filter filter = filters.get(i);
if (filter instanceof ListenableFilter) {
Filter.Listener listener = filter.listener();
if (listener != null) {
if (t == null) {
listener.onResponse(r, filterInvoker, invocation);
} else {
listener.onError(t, filterInvoker, invocation);
}
}
} else {
filter.onResponse(r, filterInvoker, invocation);
}
}
});
return asyncResult;
}

复制代码

注释1处,将执行完返回的result,经过所有监听器处理。

异步转同步(AsyncToSync)

AsyncToSyncInvoker 见名思意改invoker 是将异步的请求进行同步等待。

public Result invoke(Invocation invocation) throws RpcException{
Result asyncResult = invoker.invoke(invocation);//1
try {
if (InvokeMode.SYNC == invocation.getInvokeMode()) {
asyncResult.get(Integer.MAX_VALUE, TimeUnit.MILLISECONDS);//2
}} catch (InterruptedException e) {
throw new RpcException("Interrupted unexpectedly while waiti", e);
}
return asyncResult;}

复制代码

重点 invoke() 返回了一个 asyncResult,这是一个异步结果,此时不一定服务端就有返回,所以在注释2出,默认长时间等待,线程被挂起,一直要等到服务端返回,或者超时 才会被唤起,有打过断点的同学打到这里并不代表程序结束哈,关于超时实现我们后面文章在分析。

发起远程调用(DubboInvoker)

DubboInvoker invoker 的作用主要是获取通信实现,发起tcp请求。

//org.apache.dubbo.rpc.protocol.dubbo.DubboInvoker#doInvoke
protected Result doInvoke(final Invocation invocation) throws Throwable{
//...
ExchangeClient currentClient;
try {
int timeout = getUrl().getParameter(methodName, "timeout", 1000);
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 responseFuture = currentClient.request(inv, timeout);//2发起请求
asyncRpcResult.subscribeTo(responseFuture); FutureContext.getContext().setCompatibleFuture(responseFuture);
return asyncRpcResult;
}
}
}

复制代码

注释1 中如果设置不要求返回,直接发送请求返回结果,注释2发起请求,返回一个Future 远程调用结果。currentClient.request(inv, timeout) 我们点进去

org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeChannel#request(java.lang.Object, int)
@Override
public CompletableFuture request(Object request, int timeout) throws RemotingException{
Request req = new Request();
req.setVersion(Version.getProtocolVersion());
req.setTwoWay(true);
req.setData(request);//设置请求数据
DefaultFuture future = DefaultFuture.newFuture(channel, req, timeout);
try {
channel.send(req);//1
} catch (RemotingException e) { future.cancel(); throw e; }
return future;
}

复制代码

注释1就是netty的channel,这边就是写数据流了

public void send(Object message, boolean sent){
boolean success = true;
int timeout = 0;
try {
ChannelFuture future = channel.writeAndFlush(message);
if (sent) {
timeout = getUrl().getPositiveParameter(TIMEOUT_KEY, 1000);
success = future.await(timeout);
}
Throwable cause = future.cause();
if (cause != null) {
throw cause;
}
}
}

复制代码

到这里数据就发出去了,一直要等到服务端返回数据,netty 接收到io实践channelread 才会将数据转码,unpark 最开始我们在 AsyncToSyncInvoker 中挂起的线程。