dubbo-发起服务调用

884 阅读4分钟

前言

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<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
        List<Invoker<T>> copyInvokers = invokers;
        String methodName = RpcUtils.getMethodName(invocation);
int len = getUrl().getMethodParameter(methodName, RETRIES_KEY, DEFAULT_RETRIES) + 1;//1
        RpcException le = null;
        List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyInvokers.size()); 
        Set<String> providers = new HashSet<String>(len);
        for (int i = 0; i < len; i++) {
Invoker<T> 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<Object> 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<Object> 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 中挂起的线程。