從源碼分析RocketMQ系列-MQClientInstance類詳解

導語
  在之前的分析中,看到有一個類MQClientInstance,這個無論是在Producer端還是在Consumer端都是很重要的一個類,很多的功能都是從這個類發起的,這邊分享中就來詳細的看看這個類的功能。


  首先要知道這個類的功應該是整個的RocketMQ的消息發送的基礎,有了這個基礎之後後續的內容才能更好的被實現了,所以說,在很大程度上,對於這個類的理解有助於我們更好的瞭解RocketMQ的核心內容。所以這個系列的詳解會比較多一些,一行代碼一行代碼來分析這個類。

  這個類來自於org.apache.rocketmq.client.impl.factory包是這個包下唯一的一個類。org.apache.rocketmq.client.impl.factory.MQClientInstance。那麼下面就來進入到詳細的分析中。

基礎對象

  在之前的分析中,會看到很多的關於ConcurrentHashMap的使用,在MQClientInstance的屬性中也定義了很多的ConcurrentHashMap,分別是

  • producerTable:用來緩存Group和生產者的對應關係
  • consumerTable:用來緩存Group和消費者的對應關係
  • adminExtTable:用來緩存Group和管理員的對應關係
  • topicRouteTable:對Topic信息的緩存
  • brokerAddrTable:對於集羣中的Broker地址進行緩存
  • brokerVersionTable:對於集羣中Broker的版本信息進行緩存

private final ConcurrentMap<String/* group */, MQProducerInner> producerTable = new ConcurrentHashMap<String, MQProducerInner>();

private final ConcurrentMap<String/* group */, MQConsumerInner> consumerTable = new ConcurrentHashMap<String, MQConsumerInner>();

private final ConcurrentMap<String/* group */, MQAdminExtInner> adminExtTable = new ConcurrentHashMap<String, MQAdminExtInner>();

private final ConcurrentMap<String/* Topic */, TopicRouteData> topicRouteTable = new ConcurrentHashMap<String, TopicRouteData>();

private final ConcurrentMap<String/* Broker Name */, HashMap<Long/* brokerId */, String/* address */>> brokerAddrTable =
        new ConcurrentHashMap<String, HashMap<Long, String>>();
private final ConcurrentMap<String/* Broker Name */, HashMap<String/* address */, Integer>> brokerVersionTable =
        new ConcurrentHashMap<String, HashMap<String, Integer>>();

  從代碼中會看到上面六個緩存表分別對應了六種數據類型。分別是

  • MQProducerInner:Producer的內置對象
  • MQConsumerInner:Consumer的內置對象
  • MQAdminExtInner:管理員的內置對象
  • TopicRouteData:Topic分裝信息
  • <String/* Broker Name /, HashMap<Long/ brokerId /, String/ address */> :Broker的詳細信息
  • <String/* Broker Name /, HashMap<String/ address */, Integer>> :Broker詳細版本信息

  從上面的代碼中可以看到,這裏主要做了一個最簡單的邏輯的封裝,就是爲這些對應關係找到如下的一種關係,它最終的目的就是爲了實現,下面這種邏輯關係,而對於Broker的版本存儲,也是爲了更好的支持這種機制。在後續的分析中會對上面的這些數據提供一個詳細的分析
在這裏插入圖片描述
  從上面圖中可以看到,無論是消費者還是生產者,還是Broker NameServer,都是以集羣的形式存在的,但是這裏有一點需要說明的,無論是單點還是集羣,這個集羣內的機器標識都是可計數的。加之對集羣進行命名,對於同一個集羣內的機器數量都是可以計數的,所以說採用了ConcurrentHashMap來進行集羣信息的維護。對於不同的集羣無論這個集羣有多大,最終都可以抽象爲上面六種的數據類型。也就是說,在這個類中上面的內容都被抽象成了這個類中的對象,並且對這些對象進行的存儲。在這個類中來控制這些對象之間的調用關係,以及爲這些對象提供一些對應關係。也就是說它就是爲了實現這個流程進行服務的對象,哪裏有需要就在哪裏New一個。看上去非常的孤獨,沒有任何的父類,也沒有任何的子類。

構造方法

  在進一步瞭解之前首先來看看,這個對象的構造方法中都完成了那些操作,由於之前也分析了,這個對象就是了上面這個流程做服務的,所以說可以把它看做一個大集合,在這個集合裏面通過構造函數進行初始化了很多的東西。下面的構造方法就爲展示了很多的東西,例如ClientConfig 客戶端配置類,instanceIndex實例索引,NettyConfig Netty的相關配置類,客戶端的ClientRemotingProcessor 客戶端遠程執行,其中最爲重要的一個就是mQClientAPIImpl的設置。
在這裏插入圖片描述

配置信息

  在分析,查看源碼之後找到了如下的兩個配置類,這個兩個配置類一個是針對客戶端的配置,一個是針對Netty的配置。那麼分別來看看這兩個配置類都有那些配置以及它的實現。也就是說在這個類中提供的最多的配置就是客戶端的配置和Netty客戶端的配置。這個兩個配置就分別的堆RocketMQ作爲客戶端進行了封裝另一個方面就是對作爲Socket的Netty客戶端的配置。從這兩個角度上來看這個類就有了兩個配置的所有的優點,所有的配置都是動態的配置。


private final ClientConfig clientConfig;
private final NettyClientConfig nettyClientConfig;

加鎖操作

  除了上面的配置以外還提供了下面兩個東西,從功能的角度上就是保證了安全性,在集羣的狀態下,有多個實例需要進行管理就會涉及到多線程的問題,從它的命名角度上來看這兩個鎖似乎是爲了支持基礎功能,一個是NameServer上的註冊表的獲取,一個是心跳檢測機制。先不討論他們是什麼,先來看看這兩個對象使用的位置

private final Lock lockNamesrv = new ReentrantLock();
private final Lock lockHeartbeat = new ReentrantLock();

lockNamesrv


public boolean updateTopicRouteInfoFromNameServer(final String topic, boolean isDefault,
        DefaultMQProducer defaultMQProducer)

private void cleanOfflineBroker() 


lockHeartbeat

public void sendHeartbeatToAllBrokerWithLock()


private void unregisterClientWithLock(final String producerGroup, final String consumerGroup)


  從上面四個使用鎖的地方可以看到,分別是從遠程獲取Topic信息的時候和進行Broker心跳檢測的時候,這兩個時候由於會有多線程對當前信息進行讀寫,但是在同一時間只能有一個線程進行讀寫操作,所以這樣的操作就需要進行加鎖。對於這個重入鎖的原理在後續的分析中會提到,這裏只是對這個類進行詳細的解讀。所以這裏先不對這個機制進行說明。

服務操作

  設置完配置,設置完鎖操作之後,接下來做的事情就是一些服務的提供,這裏提供瞭如下的四個服務

  • PullMessageService :拉取消息服務
  • RebalanceService :負載均衡服務
  • DefaultMQProducer : 默認的消息生產者,這個地方需要注意一下
  • ConsumerStatsManager : 消費者狀態管理。

 private final PullMessageService pullMessageService;
 private final RebalanceService rebalanceService;
 private final DefaultMQProducer defaultMQProducer;
 private final ConsumerStatsManager consumerStatsManager;
 

  上面的四個內容前三個都還很好理解,但是第四個有點難理解,這裏我們看看在代碼中這塊內容是如何操作的


this.consumerStatsManager = new ConsumerStatsManager(this.scheduledExecutorService);

  通過查看源碼我們可以知道其實這個對象在這個類中並沒有多大的使用,但是在org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl 類中有具體的使用,但是這裏的使用也是作爲了MQClientInstance類對象的調用,其實使用的還是這個對象。這裏看到上面這個代碼中其實傳入了一個參數,這個參數表示的是如下的一個對象


private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r, "MQClientFactoryScheduledThread");
        }
    });
    

  這裏會知道ScheduledExecutorService 是一個線程池的操作,這個線程池是一個週期性執行的線程池,並且線程池中創建了一個線程對象,傳入了一個參數。schedule 方法使用各種延遲創建任務,並返回一個可用於取消或檢查執行的任務對象。scheduleAtFixedRate 和 scheduleWithFixedDelay 方法創建並執行某些在取消前一直定期運行的任務。
在這裏插入圖片描述
  如圖其實會看到在這個實例類中有很多的地方都使用了該對象,這裏先不要去管這些方法都是幹啥的,先來注意這些方法都準備幹啥,只有知道了這些才能更好的知道這個MQClientInstance對象想要幹啥,從而知道,整個的Client模塊都有那些執行操作。

實際處理操作

  在很多的高級框架中把處理問題的實際邏輯都交給xxxProcessor 這樣的對象或者是方法來執行,這個被稱爲處理器,也就是說Client實際上執行所有的處理操作都是通過這個對象來執行的。


private final ClientRemotingProcessor clientRemotingProcessor;

this.clientRemotingProcessor = new ClientRemotingProcessor(this);

this.mQClientAPIImpl = new MQClientAPIImpl(this.nettyClientConfig, this.clientRemotingProcessor, rpcHook, clientConfig);

  會看到這個對象其實就是一個上面創建下面使用的的對象,並沒有在全局的其他地方去使用到。

總結

  到這裏,會知道其實MQClientInstance類說白了就是一個客戶端的實現的轉折點,這個類中封裝了很多的基礎操作就是爲了支持上面圖中所展示的操作,也就是說這個類就是上面操作在不同的場景中的埋點有了這個類作爲基礎,就是爲後續的所有的操作打基礎,如果還有對上面圖中所展示的內容進行擴展的地方就需要基於這個類來進行擴展。在這個類的分析中也有留下的很多的問題和關鍵點,但是這些問題在後續的分享中還會不斷的被提及到,並且這些分享也會加入更多的關於編程思想或者是設計模式的東西。

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