從源碼分析RocketMQ系列-Producer的invokeSync()方法

導語
  在之前的博客中通過對於Producer中SendResult的跟蹤找到了在Client模塊下的所有的封裝以及消費的過程,深入到對接Remoting模塊的接口中對消息的封裝以及發送回收等。但是對於具體後續操作還是沒有跟進,這篇博客就從this.remotingClient.invokeSync(addr, request, timeoutMillis);方法開始進入的Message的發送、消費、持久化等功能的探討。

從invokeSync()方法開始

  在之前的分析中可以看到invokeSync()方法是來自於RemotingClient接口,並且對這個接口也有一定的說明,瞭解了這個接口只有一個實現類NettyRemotingClient,從類名上就可以看出來使用的是Netty的支持。那麼對於invokeSync()方法的支持是什麼樣呢?方法總體如下圖所示,下面就來一步步的分析該方法
在這裏插入圖片描述

1、創建一個Channel

  對於一個Channel的管理與創建,在之前分享RabbitMQ的時候簡單的提到過,這裏由於涉及到了Netty,而Netty本身就是支持了NIO的操作,既然支持了NIO的操作。這裏就需要知道Channel的創建邏輯。


final Channel channel = this.getAndCreateChannel(addr);

  代碼本身只創建了一個Channel並且通過調用了一個this.getAndCreateChannel(addr);方法來進行獲取,那麼按照之前創建Channel的邏輯,首先需要建立一個Connection,然後在利用這個Connection去複用一些Channel。那麼猜測this.getAndCreateChannel(addr);這個方法中一定有關於Connection的創建邏輯。進入該方法。

 private Channel getAndCreateChannel(final String addr) throws InterruptedException {
    if (null == addr) {
        return getAndCreateNameserverChannel();
    }
    ChannelWrapper cw = this.channelTables.get(addr);
    if (cw != null && cw.isOK()) {
        return cw.getChannel();
    }
    return this.createChannel(addr);
}
    

  從方法體重可以看到還需要進行新的判斷,並且有ChannelWrapper這樣一個封裝對象。這樣從上面的邏輯來看真正實現第一次創建的是通過this.createChannel(addr);方法來進行操作的。果然在這個方法中找到了如下的一個操作,用過Netty的都知道這個操作是什麼意思?這個操作就是爲NIO操作創建一個Bootstrap。對於這塊內容在後面邏輯中整合進行分析。這裏首先知道這個方法是提供了故意而Channel的連接。至於其他的操作,都放到後續的對於Netty使用以及對於NIO詳細說明來分析。這裏主要是分析Message的流轉。

 if (createNewConnection) {
    ChannelFuture channelFuture = this.bootstrap.connect(RemotingHelper.string2SocketAddress(addr));
    log.info("createChannel: begin to connect remote host[{}] asynchronously", addr);
    cw = new ChannelWrapper(channelFuture);
    this.channelTables.put(addr, cw);
}

2、調用一個前置的RPCHook

  上這個分析就是創建了一個Channel的工作,這個Channel就是需要通信的信道。一般的操作都是由這個Channel來完成對於網絡請求的發送和接收操作。


doBeforeRpcHooks(addr, request);


protected void doBeforeRpcHooks(String addr, RemotingCommand request) {
    if (rpcHooks.size() > 0) {
        for (RPCHook rpcHook: rpcHooks) {
            rpcHook.doBeforeRequest(addr, request);
        }
    }
}

  在很多的框架代碼中有一個統一的規則就是將實際的工作交給一個do開頭的方法來進行操作。這裏在消息請求到Consumer之前先做了一個前置的Hook,這裏主要完成的工作就是對這個Request進行一個屬性的擴展操作。會看到其實在調用發送方法之前對於Request添加了很多的擴展屬性。
在這裏插入圖片描述

3、調用發送方法
  爲了實現方法的解耦操作,這裏使用了下面這個方法對Request消息進行發送。這個Request就是通過Client端進行了封裝的操作。這裏調用的同步模式,所以先分析關於同步調用的方法


RemotingCommand response = this.invokeSyncImpl(channel, request, timeoutMillis - costTime);

  既然怒到這個方法之後最爲重要的一個點就是ResponseFuture對象,在之前的博客中有一篇是關於分析Future模式的,對於Future模式,簡單的說就是我們想要的結果。那麼這裏是對這個結果進行了封裝,但是實際上通過Netty調用的Future並不是這個。關於這方面的知識可以瞭解關於Netty的相關知識這裏不做過多的說明,有需要的話後續的分析中會提到。
在這裏插入圖片描述
  到這裏所有的消息都通過 channel.writeAndFlush(request).addListener(new ChannelFutureListener())操作發送到了Channel中開水進入到Broker進行消息進步操作。後續的操作就是從ResponseFuture對象中,獲取到我們預期的RemotingCommand Response。並且對這個Response進行了檢查。

4、調用後置的RPCHook
  這個調用的深入會發現這個方法並沒有被實現,是個空方法沒有方法體。


doAfterRpcHooks(RemotingHelper.parseChannelRemoteAddr(channel), request, response);

總結

  從invokeSync()方法開始,描述了整個的消息核心發送過程,調用很清晰,實現的功能也比較簡單。理解這個裏需要對Netty有關的知識和NIO有關的知識做一定的瞭解。後續的分享中也會涉及到有關知識的討論。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章