RabbitMQ——鏡像隊列的數據流

【概述】

rabbitmq採用了鏡像隊列的方式實現隊列的高可用,鏡像隊列的使用、配置、內部實現原理網上有很多文章都有介紹,自己很久之前也曾總結過相關內容。但回過頭再來看鏡像隊列,仍然有新的發現,本文分析總結了鏡像隊列生產消費的數據流以及節點流量,以便更好的使用鏡像隊列。

【生產消費的數據流】

分析鏡像隊列之前,我們還是先一步步從單機情況下,集羣情況下看看生產消費的數據流走向是怎樣的,最後再分析鏡像隊列的場景。

  • 單機場景:

如上圖所示,生產者,消費者連接到rabbitmq後,在rabbitmq內部會創建對應的connection,channel進程。connection進程從socket上接收生產者發送消息後投遞到channel進程;在channel進程中,根據消息發送的exchange與消息的routing-key,在內部數據庫的路由表中,查找所有匹配的隊列的進程PID,然後將消息投遞到這些隊列的進程中。

同樣,隊列進程收到消息後,找到消費者對應的channel進程PID,然後將消息投遞給該進程,channel進程再發送給對應的connection進程,然後通過socket發送給消費者。

從上面可以看出,一條消息從生產到消費,rabbitmq的節點上會有1進1出的流量。

補充說明下:在rabbitmq內部會爲每個tcp鏈接創建多個進程,其中包括專門用於數據接收的rabbit_reader進程,專門用於數據發送的rabbit_writer進程,本文爲了方便表達,統一使用connection進程進行socket數據的收發。所以上面提到的connection進程可以理解爲包括了rabbit_reader,rabbit_writer進程。

  • 集羣場景

我們都知道,每個隊列都有一個唯一的master進程,所有生產消費的消息都是由master進程負責處理的。

在集羣場景中,生產者消費者連接到集羣中的任意節點都可以正確將消息投遞到隊列和正確從隊列消費消息。如果生產者消費者連接的節點恰好是隊列master所在的節點,那麼這種情況下和單機場景是一樣的。但更多的時候會存在這樣的情況:生產者、消費者連接的rabbitmq節點並不是隊列master進程所在的節點,那麼生產消費的消息數據就會在集羣的這兩個節點上傳輸。

如上圖所示,生產消費的流程其實和單機場景一模一樣,在channel進程中查數據庫找到匹配的隊列的進程PID,然後將消息投遞到這個隊列進程中。

但是,這裏的隊列進程是在另外一個節點上rabbitmq會自動識別這一點然後通過與對端節點的分佈式端口(默認爲25672)通信,將消息發送到對端節點的進程中。也就是說,消息在集羣中進行了傳輸。

同樣,隊列收到消息後,找到消費者對應的channel進程PID,並將消息發送到這個進程中,這裏也會進行一次集羣通信,用於消息的傳輸。

在這種場景下,一條消息從生產到消費,上圖中node1節點是2進2出的流量,node2節點是1進1出的流量。

因此,儘可能的讓生產者、消費者與隊列master進程位於同一個節點上,可以減少集羣間的網絡交互。

  • 鏡像隊列

先來看下生產者、消費者連接的節點就是隊列master進程所在節點的情況,如下圖所示:

流程還是和之前一樣,但是不同的地方有兩點:

1) 在channel進程中,查數據庫找匹配的隊列的進程。對於鏡像隊列而言,除了所有匹配的隊列的master進程外,還包括所有的slave進程。也就是說,channel進程除了將消息發送給隊列的master進程外,還會將消息發送給隊列所有的slave進程,而slave進程肯定都是在遠端節點上,因此這裏就多了一次集羣間的網絡交互。

2)鏡像隊列的master進程收到後,需要負責將消息同步給所有的slave進程,rabbitmq採用的GM算法實現中,鏡像隊列中的master和所有slave都會發送一次消息和接收一次消息,同時還會發送一次對消息的ack,和接收一次消息的ack。(鏡像隊列的同步實現原理,這裏不展開詳細說明,可參考文章https://my.oschina.net/hncscwc/blog/186350)

綜上所述,生產者發送一條消息,隊列master進程所在節點會收到兩次:一次是生產者發送的,一次是隊列slave進程發送的;同樣也會將消息對外發送兩次:一次是生產者對應的channel進程將消息發送給隊列的slave進程;一次是隊列的master進程進行廣播同步將消息發送給slave進程。此外,鏡像隊列的GM算法實現 ,每條消息還會有額外的確認消息在集羣間進行發送。

再結合圖中的情況,一條消息從生產者到消費,Node1節點是2進3出的流量,Node2節點是2進1出的流量。

和集羣中的場景一樣,如果生產者消費者連接的節點不是鏡像隊列master進程所在的隊列,一條消息從生產到消費,生產者消費者連接的節點是3進3出,隊列master進程所在的節點是2進2出
 

【總結】

從前面的分析不難看出,鏡像隊列在集羣中所增加的網絡通信是較大的,尤其是出現跨節點通信的情況,因此,隊列數量不多,並且隊列的消息量不大的情況下,可以考慮使用;而在隊列數量較多,並且隊列都有較大的消息量的情況下使用鏡像隊列,需要在高可用,高性能之間進行權衡,因爲集羣間的網絡流量可能會是非常大的,實際測試時通過netstat發現rabbitmq的分佈式端口,其發送和接收隊列都有大量的數據堆積,導致性能出現瓶頸。

另外,鏡像隊列可以調整的參數不多,官方也在3.8.0版本中推出了新的隊列模式(quorum queue)用來替代鏡像隊列的方案。有興趣的朋友可以嚐鮮研究下。