1. 程式人生 > >Dubbo原始碼理解(3) 消費者呼叫過程

Dubbo原始碼理解(3) 消費者呼叫過程

小弟一直苦思 consumer 與provider 到底是怎麼通訊的呢,與是從網上找了一篇,覺得寫得很靠譜。自己就算總結,也未必有這個好,所以記錄下來!!

消費者呼叫流程涉及到消費者端和生產者端的互動,所以將分為三個部分來講解,分別是
-消費者發起呼叫請求
-生產者響應呼叫請求
-消費者獲取呼叫結果

消費者發起呼叫請求

之前文章中講過消費者初始化時最後返回的是一個InvokerInvocationHandler的代理物件,根據動態代理的原理,DUBBO介面的方法呼叫都會由invoke方法代理,我們來看一下其實現

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        Class<?>[] parameterTypes = method.getParameterTypes();
        if (method.getDeclaringClass() == Object.class) {
            return method.invoke(invoker, args);
        }
        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]);
        }
        return invoker.invoke(new RpcInvocation(method, args)).recreate();
    }

正常情況下的方法呼叫會走invoker.invoke(new RpcInvocation(method, args)).recreate()這個分支,首先來看new RpcInvocation(method, args)

    public RpcInvocation(Method method, Object[] arguments) {
        this(method.getName(), method.getParameterTypes(), arguments, null, null);
    }

    public RpcInvocation(String methodName, Class<?>[] parameterTypes, Object[] arguments, Map<String, String> attachments, Invoker<?> invoker) {
        this.methodName = methodName;
        this.parameterTypes = parameterTypes == null ? new Class<?>[0] : parameterTypes;
        this.arguments = arguments == null ? new Object[0] : arguments;
        this.attachments = attachments == null ? new HashMap<String, String>() : attachments;
        this.invoker = invoker;
    }

非常簡單的一個初始化賦值操作,就不做過多講解了,接著回頭看invoker.invoke(new RpcInvocation(method, args))方法,這裡的invoker之前也說過了,是一個通過SPI機制生成的物件,以預設設定的引數failover為例,這裡的invoker就是一個MockClusterInvoker物件中包含了一個FailoverClusterInvoker物件引用的類似鏈式的物件,那麼我們來詳細看看MockClusterInvokerinvoke方法

    public Result invoke(Invocation invocation) throws RpcException {
        Result result = null;
        //獲取mock屬性的值,沒有配置,預設false
        String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim(); 
        if (value.length() == 0 || value.equalsIgnoreCase("false")){
            //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:direct mock
            result = doMockInvoke(invocation, null);
        } else {
            //fail-mock
            try {
                result = this.invoker.invoke(invocation);
            }catch (RpcException e) {
                if (e.isBiz()) {
                    throw e;
                } else {
                    if (logger.isWarnEnabled()) {
                        logger.info("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " +  directory.getUrl(), e);
                    }
                    result = doMockInvoke(invocation, e);
                }
            }
        }
        return result;
    }

當沒有配置mock值時,value值得到的是預設值false,會去執行result = this.invoker.invoke(invocation)this.invoker剛才提到過了是一個FailoverClusterInvoker型別的物件,但該物件並沒有實現invoke方法,實際上該方法是繼承自父類AbstractClusterInvoker的,來看一下

    public Result invoke(final Invocation invocation) throws RpcException {

        checkWheatherDestoried();

        LoadBalance loadbalance;
        
        List<Invoker<T>> invokers = list(invocation);
        if (invokers != null && invokers.size() > 0) {
            loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
                    .getMethodParameter(invocation.getMethodName(),Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE));
        } else {
            loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(Constants.DEFAULT_LOADBALANCE);
        }
        //非同步操作預設新增invocation id
        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
        return doInvoke(invocation, invokers, loadbalance);
    }

這裡的list(invocation)方法根據invocation中的引數來獲取所有的invoker列表,就不深入講了,接著來看loadbalance物件的生成,loadbalance物件根據SPI機制生成,具體實現由loadbalance引數決定,也就是具體的負載均衡策略,DUBBO提供的實現有randomroundrobinleastactiveconsistenthash四種,其中沒有根據服務端負載進行調節的策略。其中預設實現為random,生成的loadbalance就是一個RandomLoadBalance的物件。本次只分析同步的介面呼叫方式,跳過RpcUtils.attachInvocationIdIfAsync,接著看doInvoke(invocation, invokers, loadbalance)方法,該方法實現在FailoverClusterInvoker

    public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
        List<Invoker<T>> copyinvokers = invokers;
        //檢查invokers是否為空
        checkInvokers(copyinvokers, invocation);
        //獲取重試次數
        int len = getUrl().getMethodParameter(invocation.getMethodName(), Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES) + 1;
        if (len <= 0) {
            len = 1;
        }
        // retry loop.
        RpcException le = null; // last exception.
        List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyinvokers.size()); // invoked invokers.
        Set<String> providers = new HashSet<String>(len);
        for (int i = 0; i < len; i++) {
            //重試時,進行重新選擇,避免重試時invoker列表已發生變化.
            //注意:如果列表發生了變化,那麼invoked判斷會失效,因為invoker示例已經改變
            if (i > 0) {
                checkWheatherDestoried();
                        //獲得InvokerWrapper的List
                copyinvokers = list(invocation);
                //重新檢查一下
                checkInvokers(copyinvokers, invocation);
            }
            Invoker<T> invoker = select(loadbalance, invocation, copyinvokers, invoked);
            invoked.add(invoker);
            RpcContext.getContext().setInvokers((List)invoked);
            try {
                Result result = invoker.invoke(invocation);
                if (le != null && logger.isWarnEnabled()) {
                    logger.warn("");
                }
                return result;
            } catch (RpcException e) {
                if (e.isBiz()) { // biz exception.
                    throw e;
                }
                le = e;
            } catch (Throwable e) {
                le = new RpcException(e.getMessage(), e);
            } finally {
                providers.add(invoker.getUrl().getAddress());
            }
        }
        throw new RpcException
         /**
         *   略去部分程式碼
         */
    }

這裡select(loadbalance, invocation, copyinvokers, invoked)方法根據傳入的loadbalance物件挑選出一個執行用的invoker,裡面呼叫鏈較深,在此不做詳細分析。最終將通過invoker.invoke(invocation)進行呼叫並返回一個Result型別的物件,也就是最終的執行結果,這裡的invoker物件是InvokerWrapper的例項,該例項引用了一個ListenerInvokerWrapper的例項,接著又鏈式引用了AbstractInvoker的例項,因此最終執行的invoke方法在AbstractInvoker中,來看一下

    public Result invoke(Invocation inv) throws RpcException {
        if(destroyed) {
            throw new RpcException("略");
        }
        RpcInvocation invocation = (RpcInvocation) inv;
        invocation.setInvoker(this);
        if (attachment != null && attachment.size() > 0) {
            invocation.addAttachmentsIfAbsent(attachment);
        }
        Map<String, String> context = RpcContext.getContext().getAttachments();
        if (context != null) {
            invocation.addAttachmentsIfAbsent(context);
        }
        if (getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)){
            invocation.setAttachment(Constants.ASYNC_KEY, Boolean.TRUE.toString());
        }
        //非同步操作預設新增invocation id
        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
        
        
        try {
            return doInvoke(invocation);
        } catch (InvocationTargetException e) { // biz exception
         /**
         *   略去部分程式碼
         */
        }
    }

這裡的關鍵方法是doInvoke(invocation),其實現在具體的Invoker實現類中,這裡我們採用的是預設的dubbo協議,所以實現類為DubboInvoker,來看看其doInvoke方法

    @Override
    protected Result doInvoke(final Invocation invocation) throws Throwable {
        RpcInvocation inv = (RpcInvocation) invocation;
        final String methodName = RpcUtils.getMethodName(invocation);
        inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());
        inv.setAttachment(Constants.VERSION_KEY, version);
        
        ExchangeClient currentClient;
        //消費者初始化時與服務端建立的連線
        if (clients.length == 1) {
            currentClient = clients[0];
        } else {
            currentClient = clients[index.getAndIncrement() % clients.length];
        }
        try {
            boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
            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);
                RpcContext.getContext().setFuture(null);
                return new RpcResult();
            } else if (isAsync) {
                ResponseFuture future = currentClient.request(inv, timeout) ;
                RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
                return new RpcResult();
            } else {
                RpcContext.getContext().setFuture(null);
                return (Result) currentClient.request(inv, timeout).get();
            }
         /**
         *   略去部分程式碼
         */
    }

這裡的isOnewayisAsync兩個標誌位分別區分單向呼叫(不在乎呼叫結果)和非同步呼叫,這裡我們分析同步呼叫的流程,這裡的currentClient是一個ReferenceCountExchangeClient型別的物件

    public ResponseFuture request(Object request) throws RemotingException {
        return client.request(request);
    }

這裡的client是一個HeaderExchangeClient型別的物件,

    public ResponseFuture request(Object request) throws RemotingException {
        return channel.request(request);
    }

這裡的channel是一個HeaderExchangeChannel型別的物件,繼續跟進去

    public ResponseFuture request(Object request) throws RemotingException {
        return request(request, channel.getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT));
    }

    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("2.0.0");
        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;
    }

這裡的request方法自己又進行了一次內部呼叫,可以看到具體實現時建立了一個DefaultFuture物件並且通過channel.send(req)方法傳送請求到生產者端,這裡不做具體深入了。接著我們跳回DubboInvokerdoInvoke方法中的currentClient.request(inv, timeout).get(),這裡是不是和jdk中future的用法很像,事實上這裡也確實是通過get方法的呼叫將執行緒阻塞在這裡等待結果,從而將非同步呼叫轉化為同步。為了證實這個想法,我們來看看DefaultFutureget方法

    public Object get() throws RemotingException {
        return get(timeout);
    }

    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);
                    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();
    }

done.await(timeout, TimeUnit.MILLISECONDS)可以看到這裡不僅是等待isDone()這個狀態位,同時還有超時時間的限制。isDone()判斷的是什麼,來看一下

    public boolean isDone() {
        return response != null;
    }

判斷response物件是否為空,那麼後面的流程其實不難猜,生產者處理完結果會來填充response

生產者響應呼叫請求

生產者開啟了埠監聽,訊息的解碼由Netty處理,解碼後交由NettyHandlermessageReceived方法進行業務處理,來看一下

    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
        try {
            handler.received(channel, e.getMessage());
        } finally {
            NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
        }
    }

先來看一下NettyChannel.getOrAddChannel

    static NettyChannel getOrAddChannel(org.jboss.netty.channel.Channel ch, URL url, ChannelHandler handler) {
        if (ch == null) {
            return null;
        }
        NettyChannel ret = channelMap.get(ch);
        if (ret == null) {
            NettyChannel nc = new NettyChannel(ch, url, handler);
            if (ch.isConnected()) {
                ret = channelMap.putIfAbsent(ch, nc);
            }
            if (ret == null) {
                ret = nc;
            }
        }
        return ret;
    }

主要是從channelMap中獲取對應的NettyChannel,接著回到NettyHandlermessageReceived方法來看handler.received(channel, e.getMessage()),這裡的handler是一個NettyServer的例項,但它本身沒有實現received方法,該方法要追溯到它的父類的父類的父類(真的就是這麼長的繼承關係。。。)AbstractPeer中,來看一下

    public void received(Channel ch, Object msg) throws RemotingException {
        if (closed) {
            return;
        }
        handler.received(ch, msg);
    }

這裡的handlerMultiMessageHandler物件的例項,來看一下其received方法的實現

    @Override
    public void received(Channel channel, Object message) throws RemotingException {
        if (message instanceof MultiMessage) {
            MultiMessage list = (MultiMessage)message;
            for(Object obj : list) {
                handler.received(channel, obj);
            }
        } else {
            handler.received(channel, message);
        }
    }

這裡的handler又是HeartbeatHandler類的例項

    public void received(Channel channel, Object message) throws RemotingException {
        setReadTimestamp(channel);
        if (isHeartbeatRequest(message)) {
            Request req = (Request) message;
            if (req.isTwoWay()) {
                Response res = new Response(req.getId(), req.getVersion());
                res.setEvent(Response.HEARTBEAT_EVENT);
                channel.send(res);
                if (logger.isInfoEnabled()) {
                    int heartbeat = channel.getUrl().getParameter(Constants.HEARTBEAT_KEY, 0);
                    if(logger.isDebugEnabled()) {
                        logger.debug("Received heartbeat from remote channel " + channel.getRemoteAddress()
                                        + ", cause: The channel has no data-transmission exceeds a heartbeat period"
                                        + (heartbeat > 0 ? ": " + heartbeat + "ms" : ""));
                    }
                }
            }
            return;
        }
        if (isHeartbeatResponse(message)) {
            if (logger.isDebugEnabled()) {
                logger.debug(
                    new StringBuilder(32)
                        .append("Receive heartbeat response in thread ")
                        .append(Thread.currentThread().getName())
                        .toString());
            }
            return;
        }
        handler.received(channel, message);
    }

因為不是心跳類的訊息,所以執行handler.received(channel, message)繼續這個呼叫鏈,這裡的handlerAllChannelHandler型別的

    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) {
            throw new ExecutionException(message, channel, getClass() + " error when process received event .", t);
        }
    }

這裡終於結束了呼叫鏈,轉而啟動了一個執行緒池來執行任務,那我們來看看具體的任務執行緒ChannelEventRunnable中到底需要執行什麼任務

    public void run() {
        switch (state) {
            case CONNECTED:
                try{
                    handler.connected(channel);
                }catch (Exception e) {
                    logger.warn("ChannelEventRunnable handle " + state + " operation error, channel is " + channel, e);
                }
                break;
            case DISCONNECTED:
                try{
                    handler.disconnected(channel);
                }catch (Exception e) {
                    logger.warn("ChannelEventRunnable handle " + state + " operation error, channel is " + channel, e);
                }
                break;
            case SENT:
                try{
                    handler.sent(channel,message);
                }catch (Exception e) {
                    logger.warn("ChannelEventRunnable handle " + state + " operation error, channel is " + channel
                            + ", message is "+ message,e);
                }
                break;
            case RECEIVED:
                try{
                    handler.received(channel, message);
                }catch (Exception e) {
                    logger.warn("ChannelEventRunnable handle " + state + " operation error, channel is " + channel
                            + ", message is "+ message,e);
                }
                break;
            case CAUGHT:
                try{
                    handler.caught(channel, exception);
                }catch (Exception e) {
                    logger.warn("ChannelEventRunnable handle " + state + " operation error, channel is "+ channel
                            + ", message is: " + message + ", exception is " + exception,e);
                }
                break;
            default:
                logger.warn("unknown state: " + state + ", message is " + message);
        }
    }

這裡傳入的是RECEIVED狀態,執行對應分支又是呼叫handler.received(channel, message),好吧繼續。。。
這裡的handlerDecodeHandler的例項,繼續跟下去

    public void received(Channel channel, Object message) throws RemotingException {
        if (message instanceof Decodeable) {
            decode(message);
        }

        if (message instanceof Request) {
            decode(((Request)message).getData());
        }

        if (message instanceof Response) {
            decode( ((Response)message).getResult());
        }

        handler.received(channel, message);
    }

呼叫鏈還在繼續,這次的handlerHeaderExchangeHandler型別

    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);
        }
    }

正常同步請求會開始執行handleRequest(exchangeChannel, request)處理請求,並通過channel.send(response)回覆結果,來重點看一下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;
    }

可以看出正常請求將由handler.reply(channel, msg)處理,這裡的handlerDubboProtocol中的一個ExchangeHandlerAdapter實現,其reply方法如下

        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());
        }

這裡一共做了兩件事,先通過getInvoker(channel, inv)獲取具體的invoker,再通過invoker.invoke(inv)執行獲取結果,先來看一下getInvoker(channel, inv)

    Invoker<?> getInvoker(Channel channel, Invocation inv) throws RemotingException{
        boolean isCallBackServiceInvoke = false;
        boolean isStubServiceInvoke = false;
        int port = channel.getLocalAddress().getPort();
        String path = inv.getAttachments().get(Constants.PATH_KEY);
        //如果是客戶端的回撥服務.
        isStubServiceInvoke = Boolean.TRUE.toString().equals(inv.getAttachments().get(Constants.STUB_EVENT_KEY));
        if (isStubServiceInvoke){
            port = channel.getRemoteAddress().getPort();
        }
        //callback
        isCallBackServiceInvoke = isClientSide(channel) && !isStubServiceInvoke;
        if(isCallBackServiceInvoke){
            path = inv.getAttachments().get(Constants.PATH_KEY)+"."+inv.getAttachments().get(Constants.CALLBACK_SERVICE_KEY);
            inv.getAttachments().put(IS_CALLBACK_SERVICE_INVOKE, Boolean.TRUE.toString());
        }
        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();
    }

這裡又看到了熟悉的exporterMap,之前講生產者初始化的時候就說過這個map中放入了封裝過的Invoker物件exporter,現在又把它取了出了並通過getInvoker()方法獲得封裝在其中的Invoker物件。
接著來看invoker.invoke(inv)方法,其實現首先在InvokerWrapper類中

    public Result invoke(Invocation invocation) throws RpcException {
        return invoker.invoke(invocation);
    }

然後會呼叫到AbstractProxyInvoker中的invoke方法

    public Result invoke(Invocation invocation) throws RpcException {
        try {
            return new RpcResult(doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments()));
        } catch (InvocationTargetException e) {
            return new RpcResult(e.getTargetException());
        } catch (Throwable e) {
            throw new RpcException("Failed to invoke remote proxy method " + invocation.getMethodName() + " to " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }

這裡doInvoke方法的實現在JavassistProxyFactorygetInvoker方法中

    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // TODO Wrapper類不能正確處理帶$的類名
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
        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);
            }
        };
    }

這裡根據傳入的 proxy物件的類資訊建立對它的包裝物件Wrapper
並呼叫其invokeMethod方法,通過傳入的引數來呼叫proxy物件的對應方法,返回呼叫結果,也就是執行具體的業務。
完成handleRequest(exchangeChannel, request)方法的解析後,回到HeaderExchangeHandler類中接著來看一下channel.send(response),這裡的channel傳入的是NettyChannel型別的物件,send方法的實現在其父類的父類AbstractPeer中,來看一下

    public void send(Object message) throws RemotingException {
        send(message, url.getParameter(Constants.SENT_KEY, false));
    }

其具體實現又在NettyChannel

    public void send(Object message, boolean sent) throws RemotingException {
        super.send(message, sent);
        
        boolean success = true;
        int timeout = 0;
        try {
            ChannelFuture future = channel.write(message);
            if (sent) {
                timeout = getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
                success = future.await(timeout);
            }
            Throwable cause = future.getCause();
            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");
        }
    }

可以看到業務處理結果最後通過ChannelFuture物件進行了傳送,到此生產者端的任務就完成了。

消費者獲取呼叫結果

這裡消費者端通過NETTY從生產者端獲取資料的流程和之前的如出一轍,呼叫鏈直到HeaderExchangeHandler之前都是一樣的,我們先來回顧一下HeaderExchangeHandlerreceived方法

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);
        }
    }

之前走的是Request分支,這次因為是響應訊息走的是Response分支,那麼來看一下handleResponse(channel, (Response) message)的具體實現

    static void handleResponse(Channel channel, Response response) throws RemotingException {
        if (response != null && !response.isHeartbeat()) {
            DefaultFuture.received(channel, response);
        }
    }

繼續跟進去看received方法

    public static void received(Channel channel, Response response) {
        try {
            DefaultFuture future = FUTURES.remove(response.getId());
            if (future != null) {
                future.doReceived(response);
            } else {
                logger.warn("The timeout response finally returned at " 
                            + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())) 
                            + ", response " + response 
                            + (channel == null ? "" : ", channel: " + channel.getLocalAddress() 
                                + " -> " + channel.getRemoteAddress()));
            }
        } finally {
            CHANNELS.remove(response.getId());
        }
    }

繼續看doReceived幹了什麼

    private void doReceived(Response res) {
        lock.lock();
        try {
            response = res;
            if (done != null) {
                done.signal();
            }
        } finally {
            lock.unlock();
        }
        if (callback != null) {
            invokeCallback(callback);
        }
    }

看到這裡把執行結果賦值給response,正好應證了我們之前的猜想,消費者的同步阻塞也就可以繼續執行下去了,這也算是非常經典的非同步轉同步的實現方案了吧。

總結

本文把消費者端和生產者端互動的大概流程進行了講解,流程主要分為三個部分,分別是:消費者發起呼叫請求、生產者響應呼叫請求和消費者獲取呼叫結果,概括一下就是消費者通過生成的代理物件呼叫invoke方法通過Netty的通道去請求生產者的exporter進行執行,並且通過future的方式將非同步的互動轉為了同步響應。