六 捋代碼--dubbo源碼調用之服務端

說完調用的消費端,下面該說說請求到了服務端,是怎么處理的了。首先讓我們回顧下服務端服務發布模型圖


dubbo服務端模型

根據netty通信的知識和模型圖,我們知道此時服務端接收到消息會調用NettyServerHandler的channelRead方法

  public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
        try {
            handler.received(channel, msg);
        } finally {
            NettyChannel.removeChannelIfDisconnected(ctx.channel());
        }
    }

貌似和消費端上章我們分析端類似,依次類推,我們一樣會找到AllChannelHandler類

   public void received(Channel channel, Object message) throws RemotingException {
        ExecutorService cexecutor = getExecutorService();
        try {
            cexecutor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message));
        } catch (Throwable t) {
            //TODO 臨時解決線程池滿后異常信息無法發送到對端的問題。待重構
            //fix 線程池滿了拒絕調用不返回,導致消費者一直等待超時
            if(message instanceof Request && t instanceof RejectedExecutionException){
                Request request = (Request)message;
                if(request.isTwoWay()){
                    String msg = "Server side(" + url.getIp() + "," + url.getPort() + ") threadpool is exhausted ,detail msg:" + t.getMessage();
                    Response response = new Response(request.getId(), request.getVersion());
                    response.setStatus(Response.SERVER_THREADPOOL_EXHAUSTED_ERROR);
                    response.setErrorMessage(msg);
                    channel.send(response);
                    return;
                }
            }
            throw new ExecutionException(message, channel, getClass() + " error when process received event .", t);
        }
    }

一樣的邏輯,通過ChannelEventRunnable異步線程進行消息處理,按模型圖走,一樣會走到HeaderExchangeHandler類中

  public void received(Channel channel, Object message) throws RemotingException {
        channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
        ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel);
        try {
            if (message instanceof Request) {
                // handle request.
                Request request = (Request) message;
                if (request.isEvent()) {
                    handlerEvent(channel, request);
                } else {
                    if (request.isTwoWay()) {
                        Response response = handleRequest(exchangeChannel, request);
                        channel.send(response);
                    } else {
                        handler.received(exchangeChannel, request.getData());
                    }
                }
            } else if (message instanceof Response) {
                handleResponse(channel, (Response) message);
            } else if (message instanceof String) {
                if (isClientSide(channel)) {
                    Exception e = new Exception("Dubbo client can not supported string message: " + message + " in channel: " + channel + ", url: " + channel.getUrl());
                    logger.error(e.getMessage(), e);
                } else {
                    String echo = handler.telnet(channel, (String) message);
                    if (echo != null && echo.length() > 0) {
                        channel.send(echo);
                    }
                }
            } else {
                handler.received(exchangeChannel, message);
            }
        } finally {
            HeaderExchangeChannel.removeChannelIfDisconnected(channel);
        }
    }

處理邏輯

                   if (request.isTwoWay()) {
                        Response response = handleRequest(exchangeChannel, request);
                        channel.send(response);
                    } else {
                        handler.received(exchangeChannel, request.getData());
                    }

當是雙向時會調用有返回結果到方法handleRequest

   Response handleRequest(ExchangeChannel channel, Request req) throws RemotingException {
        Response res = new Response(req.getId(), req.getVersion());
        if (req.isBroken()) {
            Object data = req.getData();

            String msg;
            if (data == null) msg = null;
            else if (data instanceof Throwable) msg = StringUtils.toString((Throwable) data);
            else msg = data.toString();
            res.setErrorMessage("Fail to decode request due to: " + msg);
            res.setStatus(Response.BAD_REQUEST);

            return res;
        }
        // find handler by message class.
        Object msg = req.getData();
        try {
            // handle data.
            Object result = handler.reply(channel, msg);
            res.setStatus(Response.OK);
            res.setResult(result);
        } catch (Throwable e) {
            res.setStatus(Response.SERVICE_ERROR);
            res.setErrorMessage(StringUtils.toString(e));
        }
        return res;
    }

這時候我們看到new了一個Response類,他到id就是request到id,對應上我們消費端返回時到雙線程通信問題了。最后調用落到 handler.reply(channel, msg)方法,模型圖顯示到了DubboProtocol$requestHandler類中

      public Object reply(ExchangeChannel channel, Object message) throws RemotingException {
          if (message instanceof Invocation) {
              Invocation inv = (Invocation) message;
              //獲取通道緩存對應的invoker
              Invoker<?> invoker = getInvoker(channel, inv);
              //如果是callback 需要處理高版本調用低版本的問題
              if (Boolean.TRUE.toString().equals(inv.getAttachments().get(IS_CALLBACK_SERVICE_INVOKE))) {
                  String methodsStr = invoker.getUrl().getParameters().get("methods");
                  boolean hasMethod = false;
                  if (methodsStr == null || methodsStr.indexOf(",") == -1) {
                      hasMethod = inv.getMethodName().equals(methodsStr);
                  } else {
                      String[] methods = methodsStr.split(",");
                      for (String method : methods) {
                          if (inv.getMethodName().equals(method)) {
                              hasMethod = true;
                              break;
                          }
                      }
                  }
                  if (!hasMethod) {
                      logger.warn(new IllegalStateException("The methodName " + inv.getMethodName() + " not found in callback service interface ,invoke will be ignored. please update the api interface. url is:" + invoker.getUrl()) + " ,invocation is :" + inv);
                      return null;
                  }
              }
              RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
              return invoker.invoke(inv);
          }
          throw new RemotingException(channel, "Unsupported request: " + message == null ? null : (message.getClass().getName() + ": " + message) + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress());
      }

我們可以看到, Invoker<?> invoker = getInvoker(channel, inv)方法去獲取了我們發布時生成的invoker,這個invoker是誰?對,就是緩存在exporterMap的DubboExporter重點invoker對象


    Invoker<?> getInvoker(Channel channel, Invocation inv) throws RemotingException {
      .......
        String serviceKey = serviceKey(port, path, inv.getAttachments().get(Constants.VERSION_KEY), inv.getAttachments().get(Constants.GROUP_KEY));

        DubboExporter<?> exporter = (DubboExporter<?>) exporterMap.get(serviceKey);

        if (exporter == null)
            throw new RemotingException(channel, "Not found exported service: " + serviceKey + " in " + exporterMap.keySet() + ", may be version or group mismatch " + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress() + ", message:" + inv);

        return exporter.getInvoker();
    }

找到它就好辦了,結合模型圖和我們分析的發布知識,我們可以看到


模型圖invoker部分

最后調用了當時proxyFactory生成的抽象類中

return new AbstractProxyInvoker<T>(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName,
                                      Class<?>[] parameterTypes,
                                      Object[] arguments) throws Throwable {
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };

而此類的實際調用者是我們生成的一個泛式的wrapper

public class Wrapper1 extends Wrapper {

    public static String[] pns;
    public static java.util.Map pts;
    public static String[] mns = new String[]{"sayHello"};
    public static String[] dmns = new String[]{"sayHello"};
    public static Class[] mts0;

    public String[] getPropertyNames() {
        return pns;
    }

    public boolean hasProperty(String n) {
        return pts.containsKey(n);
    }

    public Class getPropertyType(String n) {
        return (Class) pts.get(n);
    }

    public String[] getMethodNames() {
        return mns;
    }

    public String[] getDeclaredMethodNames() {
        return dmns;
    }

    public void setPropertyValue(Object o, String n, Object v) {
        com.alibaba.dubbo.kai.api.imp.HelloApiImpl w;
        try {
            w = ((com.alibaba.dubbo.kai.api.imp.HelloApiImpl) o);
        } catch (Throwable e) {
            throw new IllegalArgumentException(e);
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + n + "\" filed or setter method in class com.alibaba.dubbo.kai.api.imp.HelloApiImpl.");
    }


    public Object getPropertyValue(Object o, String n) {
        com.alibaba.dubbo.kai.api.imp.HelloApiImpl w;
        try {
            w = ((com.alibaba.dubbo.kai.api.imp.HelloApiImpl) o);
        } catch (Throwable e) {
            throw new IllegalArgumentException(e);
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + n + "\" filed or setter method in class com.alibaba.dubbo.kai.api.imp.HelloApiImpl.");
    }

    public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException {
        com.alibaba.dubbo.kai.api.imp.HelloApiImpl w;
        try {
            w = ((com.alibaba.dubbo.kai.api.imp.HelloApiImpl) o);
        } catch (Throwable e) {
            throw new IllegalArgumentException(e);
        }
        try {
            //和cglib寫法類似,不通過反射調用,效率更高
            if ("sayHello".equals(n) && p.length == 0) {
                return  w.sayHello();
            }
        } catch (Throwable e) {
            throw new java.lang.reflect.InvocationTargetException(e);
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchMethodException("Not found method \"" + n + "\" in class com.alibaba.dubbo.kai.api.imp.HelloApiImpl.");
    }
}

調用他的invokeMethod方法,顯示最后直接調用了ref(我們實際業務對象)的相應方法,最后返回業務結果。
獲取到結果后,我們返回到HeaderExchangeHandler到received方法

  Response response = handleRequest(exchangeChannel, request);
  channel.send(response);

下面就是發送結果了,那么根據我們的分析channel應該是對應的NettyChannel

  public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //包裝后的NettyChannel
        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
        try {
            handler.received(channel, msg);
        } finally {
            NettyChannel.removeChannelIfDisconnected(ctx.channel());
        }
    }

調用NettyChannel的send方法

 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);
            if (sent) {
                timeout = getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
                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);
        }

        if (!success) {
            throw new RemotingException(this, "Failed to send message " + message + " to " + getRemoteAddress()
                    + "in timeout(" + timeout + "ms) limit");
        }
    }

這就和上一章一樣,且對應上了上章的消費端接收。

到此,調用的消費端和服務端我們就都分析完了。最后再回到我們開章的調用時序圖,就基本已經分析完了。


dubbo調用時許圖

好了,dubbo基本調用完成,但其中我們還有些遺留

  • 負載均衡算法
  • 各個filter實現作用
  • 其他協議但實現概覽
  • 監控但實現
  • 其他注冊中心的實現
    以后有時間大家再一起學習下其他內容。

下一章 ??? 手寫dubbo核心調用
首頁 ??? dubbo源碼欣賞簡介

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容