【RocketMQ源碼深度解析2】源碼目錄結構介紹&Remoting通信層

源碼目錄結構介紹&Remoting通信層

一:源碼目錄結構介紹

RocketMQ源碼分爲以下幾個package:

  • rocketmq-broker:整個mq的核心,他能夠接受producer和consumer的請求,並調用store層服務對消息進行處理。HA服務的基本單元,支持同步雙寫,異步雙寫等模式。
  • rocketmq-clien::mq客戶端實現,目前官方僅僅開源了java版本的mq客戶端,c++,go客戶端有社區開源貢獻。
  • rocketmq-common:一些模塊間通用的功能類,比如一些配置文件、常量。
  • rocketmq-example:官方提供的例子,對典型的功能比如order message,push consumer,pull consumer的用法進行了示範。
  • rocketmq-filtersrv:消息過濾服務,相當於在broker和consumer中間加入了一個filter代理。
  • rocketmq-remoting:基於netty的底層通信實現,所有服務間的交互都基於此模塊。
  • rocketmq-srvut:解析命令行的工具類。
  • `rocketmq-store:存儲層實現,同時包括了索引服務,高可用HA服務實現。
  • rocketmq-tools:mq集羣管理工具,提供了消息查詢等功能。

二:rocketmq-remoting通信層介紹

remoting模塊是mq的基礎通信模塊,理解通信層的原理對理解模塊間的交互很有幫助。底層基於Netty網絡庫驅動,因此需要先了解一些基本的Netty原理。

1、Netty基本知識

nettyThread 
首先,我們需要了解Netty的線程模型:Netty運用了reactor模式,採用了監聽線程池IO線程池分離的思想,數據的流轉在Netty中採取了類似職責鏈的設計模式,因此數據看起來就像在管道中流動一樣了。
現在我們只需要知道,我們能夠定義自己的handler並插入管道即可實現對數據的操作了。目前大致瞭解即可,稍後我們會結合mq的代碼講解。


2、mq通信協議

(1) 消息格式

proto 
重點關注一下header字段,他有2種編碼方式,一種JSON格式,另一種是ROCKETMQ格式。重點關注JSON格式: 

header 
這裏直接引用了官方文檔裏的圖片,RequestCode.javaResponseCode.java文件包含了所有的操作碼,推薦調試2個模塊之間的通信的時候可以以操作碼爲索引。一個實際的請求如圖: 

header_actual
code=103表示他是一個REGISTER_BROKER消息 

(2) mq的消息處理邏輯

那麼,對於一個實際的請求,mq是如何進行編解碼以及分發請求的呢?比較重要的兩個類包括NettyRemotingClient和NettyRemotingServer,這裏以NettyRemotingServer爲例子先看它的啓動: 
remotingServer_start
可以看到ch.pipeline().addLast就是往管道里添加數據的處理邏輯,首先需要知道對於每一個事件處理器handler,他可以處理的事件包括了以下幾種(覆蓋父類方法即可實現),只要滿足條件數據會經過每一個handler對應的事件處理方法:

  • channelActivechannelInactive連接建立連接關閉的時候會被回調。
  • channelRead:當channel有數據可讀時會回調到這個函數。mq正是從這個函數將請求分發到後端線程進行處理的。
  • exceptionCaught:發生異常時回調。
  • userEventTriggered:當上面的事件都不滿足自己的需求時,用戶可以在這裏面自定義的事件處理方法。 

    現在回頭來看,mq的pipeline管道定義如下handler的含義:
  • NettyEncoderNettyDecoder:mq對應的編碼器和解碼器的邏輯,他們分別覆蓋了父類的encodedecode方法。
  • IdleStateHandler:Netty自帶的心跳管理器
  • NettyConnetManageHandle:連接管理器,他負責捕獲新連接、連接斷開、異常等事件,然後統一調度到NettyEventExecuter處理器處理。
  • NettyServerHandler:當一個消息經過前面的解碼等步驟後,然後調度到channelRead0方法,然後根據消息類型進行分發 

    繼續跟蹤NettyServerHandler代碼: 
    delegate

    (a)處理消息請求processRequestCommand
    首先看NettyRemotingAbstract類中的一個成員:

    HashMap<Integer/* request code */, Pair<NettyRequestProcessor, ExecutorService>> processorTable

    可以看到註釋,鍵表示了request code,mq中可以爲不同類型的請求碼指定不同的處理器Processor處理,但是要注意消息實際的處理並不是在當前線程,而是被封裝成task放到Processor對應的線程池處理:

    final RequestTask requestTask = new RequestTask(run, ctx.channel(), cmd);
    pair.getObject2().submit(requestTask);

    在RocketMQ中能看到很多地方都是這樣的處理,這樣的設計能夠最大程度的保證異步,保證每個線程都專注處理自己負責的東西。 以下是Processor的實現:
    processer-implement

    最後,processRequestCommand這個函數的整體處理邏輯如下所示:

Find ProcessorBuild Msg Runnable Tasksubmit Task

另外,要注意一下,第二步構建task的時候,運用了模板設計模式,在任務的執行前後加入了一個hook:我們可以利用這個hook進行一些額外的操作,比如消息的加密解密。

rpcHook.doBeforeRequest(RemotingHelper.parseChannelRemoteAddr(ctx.channel()), cmd);
Processor.processRequest()
rpcHook.doAfterResponse(RemotingHelper.parseChannelRemoteAddr(ctx.channel()), cmd, response);

(b)處理消息響應processResponseCommand
實際上,這部分的處理並不是太難,首先理解下面這個結構:

protected final ConcurrentHashMap<Integer /* opaque */, ResponseFuture> responseTable 

opaque表示請求發起連接方在同個連接上不同的請求標識代碼,每次發送一個消息的時候,他可以選擇同步阻塞的方式和異步非阻塞的方式,不管是哪種方式,他都會保存操作碼到ResponseFuture的映射。 
重點講解一下ResponseFuture這個類,這個類中比較重要的成員包括一個回調函數invokeCallback,以及一個信號量semaphore

  • 對於同步消息,這二個參數通常是個null。
  • 對於異步消息invokeCallback的作用就是在收到消息響應的時候能夠根據responseTable找到操作碼對應的回調函數;semaphore的主要作用是用作流控,當多個線程同時往一個連接寫數據時可以通過信號量控制permit同時寫許可的數量。 
    簡單來說,總體流程如下:
Startsync msg?invokeCallBackEndwakeup send threadyesno

當然,流程圖未列舉的操作還包括釋放信號量資源,以及清空responseTable表相關鍵值對信息等操作。
還需要知道的是,爲了及時的處理responseTable內的信息,有一個定時回查線程會定期回查responseTable,處理方法與上面類似,參考scanResponseTable代碼。 

·NettyRemotingClient·的處理實際上與NettyRemotingServer的處理基本一致,唯一不同的是Netty pipeline中連接管理相關的handler額外還處理了connect事件,該事件在客戶端主動連接對端成功後回調。


原文地址:http://blog.csdn.net/a2888409/article/details/53838580

另:processMessageReceived方法是外部類的方法,內部類中可以直接用方法名調用外部類的方法


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