RocketMq

ITWUYI發表於2020-10-23

RocketMQ知識點

1、簡介

(1)典型的訊息中介軟體收發訊息的模型

典型的訊息中介軟體收發訊息的模型

(2)RocketMQ是阿里推出的一款開源的訊息中介軟體。其前身是MetaQ,全名是Metamorphosis。它並不遵循任何規範(比如JMS、AMQP等),但是參考了各種規範與同類產品的設計思想,主要借鑑的產品是Apache Kafka。

2、特性

(1)它是一個佇列模型的訊息中介軟體,具有高效能、高可靠、高實時、低延時、分散式、支援非同步、同步雙寫特點。

(2)靈活可擴充套件性

RocketMQ 天然支援叢集,其核心四元件(Name Server、Broker、Producer、Consumer)每一個都可以在沒有單點故障的情況下進行水平擴充套件。

(3)能夠保證嚴格的訊息順序

可以保證訊息消費者按照訊息傳送的順序對訊息進行消費。順序訊息分為全域性有序和區域性有序,一般推薦使用區域性有序,即生產者通過將某一類訊息按順序傳送至同一個佇列來實現。

(4)多種訊息過濾方式

訊息過濾分為在伺服器端過濾和在消費端過濾。伺服器端過濾時可以按照訊息消費者的要求(支援topic型別、tag、語法表示式【filter的方式等】過濾)做過濾,優點是減少不必要訊息傳輸,缺點是增加了訊息伺服器的負擔,實現相對複雜。消費端過濾則完全由具體應用自定義實現,這種方式更加靈活,缺點是很多無用的訊息會傳輸給訊息消費者。

(5)海量訊息堆積能力(億級)

RocketMQ 採用零拷貝原理實現超大的訊息的堆積能力,據說單機已可以支援億級訊息堆積,而且在堆積了這麼多訊息後依然保持寫入低延遲。

(6)支援事務訊息

RocketMQ 除了支援普通訊息,順序訊息之外還支援事務訊息(根據offset更改msg狀態),這個特性對於分散式事務來說提供了又一種解決思路。

(7)回溯消費(獨有)

回溯消費是指消費者已經消費成功的訊息,由於業務上需求需要重新消費,RocketMQ 支援按照時間回溯消費,時間維度精確到毫秒,可以向前回溯,也可以向後回溯。

(8)定時訊息(重點)

在傳送訊息是也可以針對message設定setDelayTimeLevel,支援級別:5s,5s,1m。

(9)實時的訊息釋出/訂閱機制

(10)提供豐富的訊息拉取模式

使用長輪詢Pull方式長輪詢詳解,可保證訊息非常實時,訊息實時性不低於Push。

(11)較少的依賴

(12)支援佇列優先順序,數字表達

(13)持久化

充分利用linux系統記憶體cache提升效能。

(14)訊息失敗重試機制

針對provider的重試,當訊息傳送到選定的broker時如果出現失敗會自動選擇其他的broker進行重發,預設重試三次,當然重試次數要在訊息傳送的超時時間範圍內。

針對consumer的重試,如果訊息因為各種原因沒有消費成功,會自動加入到重試佇列,一般情況如果是因為網路等問題連續重試也是照樣失敗,所以rocketmq也是採用階梯重試的方式。

3、基礎概念

生產者

生產者(Producer)負責產生訊息,生產者向訊息伺服器傳送由業務應用程式系統生成的訊息。 RocketMQ 提供了三種方式傳送訊息:同步、非同步和單向。

同步傳送

同步傳送指訊息傳送方發出資料後會在收到接收方發回響應之後才發下一個資料包。一般用於重要通知訊息,例如重要通知郵件、營銷簡訊。

非同步傳送

非同步傳送指傳送方發出資料後,不等接收方發回響應,接著傳送下個資料包,一般用於可能鏈路耗時較長而對響應時間敏感的業務場景,例如使用者視訊上傳後通知啟動轉碼服務。

單向傳送

單向傳送是指只負責傳送訊息而不等待伺服器回應且沒有回撥函式觸發,適用於某些耗時非常短但對可靠性要求並不高的場景,例如日誌收集。

生產者組

生產者組(Producer Group)是一類 Producer 的集合,這類 Producer 通常傳送一類訊息並且傳送邏輯一致,所以將這些 Producer 分組在一起。從部署結構上看生產者通過 Producer Group 的名字來標記自己是一個叢集。

消費者

消費者(Consumer)負責消費訊息,消費者從訊息伺服器拉取資訊並將其輸入使用者應用程式。站在使用者應用的角度消費者有兩種型別:拉取型消費者、推送型消費者。

拉取型消費者

拉取型消費者(Pull Consumer)主動從訊息伺服器拉取資訊,只要批量拉取到訊息,使用者應用就會啟動消費過程,所以 Pull 稱為主動消費型。

推送型消費者

推送型消費者(Push Consumer)封裝了訊息的拉取、消費進度和其他的內部維護工作,將訊息到達時執行的回撥介面留給使用者應用程式來實現。所以 Push 稱為被動消費型別,但從實現上看還是從訊息伺服器中拉取訊息,不同於 Pull 的是 Push 首先要註冊消費監聽器,當監聽器處觸發後才開始消費訊息。

消費者組

消費者組(Consumer Group)一類 Consumer 的集合名稱,這類 Consumer 通常消費同一類訊息並且消費邏輯一致,所以將這些 Consumer 分組在一起。消費者組與生產者組類似,都是將相同角色的分組在一起並命名,分組是個很精妙的概念設計,RocketMQ 正是通過這種分組機制,實現了天然的訊息負載均衡。消費訊息時通過 Consumer Group 實現了將訊息分發到多個消費者伺服器例項,比如某個 Topic 有9條訊息,其中一個 Consumer Group 有3個例項(3個程式或3臺機器),那麼每個例項將均攤3條訊息,這也意味著我們可以很方便的通過加機器來實現水平擴充套件。

訊息伺服器

訊息伺服器(Broker)是訊息儲存中心,主要作用是接收來自 Producer 的訊息並儲存, Consumer 從這裡取得訊息。它還儲存與訊息相關的後設資料,包括使用者組、消費進度偏移量、佇列資訊等。從部署結構圖中可以看出 Broker 有 Master 和 Slave 兩種型別,Master 既可以寫又可以讀,Slave 不可以寫只可以讀。從物理結構上看 Broker 的叢集部署方式有四種:單 Master 、多 Master 、多 Master 多 Slave(同步刷盤)、多 Master多 Slave(非同步刷盤)。

單 Master

這種方式一旦 Broker 重啟或當機會導致整個服務不可用,這種方式風險較大,所以顯然不建議線上環境使用。

多 Master

所有訊息伺服器都是 Master ,沒有 Slave 。這種方式優點是配置簡單,單個 Master 當機或重啟維護對應用無影響。缺點是單臺機器當機期間,該機器上未被消費的訊息在機器恢復之前不可訂閱,訊息實時性會受影響。

多 Master 多 Slave(非同步複製)

每個 Master 配置一個 Slave,所以有多對 Master-Slave,訊息採用非同步複製方式,主備之間有毫秒級訊息延遲。這種方式優點是訊息丟失的非常少,且訊息實時性不會受影響,Master 當機後消費者可以繼續從 Slave 消費,中間的過程對使用者應用程式透明,不需要人工干預,效能同多 Master 方式幾乎一樣。缺點是 Master 當機時在磁碟損壞情況下會丟失極少量訊息。

多 Master 多 Slave(同步雙寫)

每個 Master 配置一個 Slave,所以有多對 Master-Slave ,訊息採用同步雙寫方式,主備都寫成功才返回成功。這種方式優點是資料與服務都沒有單點問題,Master 當機時訊息無延遲,服務與資料的可用性非常高。缺點是效能相對非同步複製方式略低,傳送訊息的延遲會略高。

名稱伺服器

名稱伺服器(NameServer)用來儲存 Broker 相關元資訊並給 Producer 和 Consumer 查詢 Broker 資訊。NameServer 被設計成幾乎無狀態的,可以橫向擴充套件,節點之間相互之間無通訊,通過部署多臺機器來標記自己是一個偽叢集。每個 Broker 在啟動的時候會到 NameServer 註冊,Producer 在傳送訊息前會根據 Topic 到 NameServer 獲取到 Broker 的路由資訊,Consumer 也會定時獲取 Topic 的路由資訊。所以從功能上看應該是和 ZooKeeper 差不多,據說 RocketMQ 的早期版本確實是使用的 ZooKeeper ,後來改為了自己實現的 NameServer 。

訊息

訊息(Message)就是要傳輸的資訊。一條訊息必須有一個主題(Topic),主題可以看做是你的信件要郵寄的地址。一條訊息也可以擁有一個可選的標籤(Tag)和額處的鍵值對,它們可以用於設定一個業務 key 並在 Broker 上查詢此訊息以便在開發期間查詢問題。

主題

主題(Topic)可以看做訊息的規類,它是訊息的第一級型別。比如一個電商系統可以分為:交易訊息、物流訊息等,一條訊息必須有一個 Topic 。Topic 與生產者和消費者的關係非常鬆散,一個 Topic 可以有0個、1個、多個生產者向其傳送訊息,一個生產者也可以同時向不同的 Topic 傳送訊息。一個 Topic 也可以被 0個、1個、多個消費者訂閱。

標籤

標籤(Tag)可以看作子主題,它是訊息的第二級型別,用於為使用者提供額外的靈活性。使用標籤,同一業務模組不同目的的訊息就可以用相同 Topic 而不同的 Tag 來標識。比如交易訊息又可以分為:交易建立訊息、交易完成訊息等,一條訊息可以沒有 Tag 。標籤有助於保持您的程式碼乾淨和連貫,並且還可以為 RocketMQ 提供的查詢系統提供幫助。

訊息佇列

訊息佇列(Message Queue),主題被劃分為一個或多個子主題,即訊息佇列。一個 Topic 下可以設定多個訊息佇列,傳送訊息時執行該訊息的 Topic ,RocketMQ 會輪詢該 Topic 下的所有佇列將訊息發出去。

訊息消費模式

訊息消費模式有兩種:叢集消費(Clustering)和廣播消費(Broadcasting)。預設情況下就是叢集消費,該模式下一個消費者叢集共同消費一個主題的多個佇列,一個佇列只會被一個消費者消費,如果某個消費者掛掉,分組內其它消費者會接替掛掉的消費者繼續消費。而廣播消費訊息會發給消費者組中的每一個消費者進行消費。

訊息順序

訊息順序(Message Order)有兩種:順序消費(Orderly)和並行消費(Concurrently)。順序消費表示訊息消費的順序同生產者為每個訊息佇列傳送的順序一致,所以如果正在處理全域性順序是強制性的場景,需要確保使用的主題只有一個訊息佇列。並行消費不再保證訊息順序,消費的最大並行數量受每個消費者客戶端指定的執行緒池限制。

4、訊息生命週期

對於任意一個訊息中介軟體來說,訊息都需要經過訊息生產-訊息儲存-訊息消費三個過程。其中訊息生產包括了Producer端的訊息構造和訊息傳送,訊息儲存包括了Broker端的訊息接收和訊息落地,訊息消費包括了Consumer端的訊息接收和訊息處理。不同訊息中介軟體之所以功能不同,效能差異較大,其主要原因就是訊息生命週期中各個階段的處理方式不同。下面介紹一下RocketMQ在各個階段的處理方式。

訊息生產

訊息的生產者Producer在傳送訊息時,會從Name Server上獲取訊息的目的地(Topic)在各個Broker上的狀態,如果發現同一個Broker下的Topic有多個Queue(佇列),則會根據RoundBin演算法依次向每個Queue傳送訊息,此外,如果發現多個Broker上均有相同Topic,也會依照輪詢的方式依次向這些Broker傳送訊息。

訊息儲存

RocketMQ的訊息儲存是由consume queue和commit log配合完成的。

consume queue是訊息的邏輯佇列,相當於字典的目錄,用於指定訊息在物理檔案commit log中的位置。每一個queue都有一個對應的consume queue檔案。consume queue中存放的是一串20位元組定長的二進位制資料,順序寫順序讀。
Commit Log是訊息存放的物理檔案,每臺Broker上的Commit Log被本機所有的queue共享,不做任何區分。CommitLog中訊息儲存單元長度不固定,檔案順序寫,隨機讀。

簡而言之,Broker端在收到一條訊息後,如果是訊息需要落盤,則會在Commit Log中寫入整條訊息,並在consume queue中寫入該訊息的索引資訊。訊息被消費時,則根據consume queue中的資訊去Commit Log中獲取訊息。RocketMQ在訊息被消費後,並不會去Commit Log中刪除訊息,而是會儲存3天(可配置)而後批量刪除。

RocketMQ支援同步刷盤及非同步刷盤兩種模式,同步刷盤指的是Producer將訊息傳送至Broker後,等待訊息刷入Commit Log和consume queue後才算訊息傳送成功,而非同步刷盤則是將訊息傳送至Broker後,Broker將訊息放入記憶體則告知Producer訊息傳送成功,而後由Broker自行將記憶體中的訊息批量刷入磁碟。

訊息消費

RocketMQ訊息訂閱有兩種模式,一種是Push模式,即Broker主動向消費端推送;另外一種是Pull模式,即消費端在需要時,主動到Broker拉取。但在具體實現時,Push和Pull模式都是採用消費端主動拉取的方式。

Consumer端每隔一段時間主動向broker傳送拉訊息請求,broker在收到Pull請求後,如果有訊息就立即返回資料,Consumer端收到返回的訊息後,再回撥消費者設定的Listener方法。如果broker在收到Pull請求時,訊息佇列裡沒有資料,broker端會阻塞請求直到有資料傳遞或超時才返回。

與Kafka類似,Kafka中Consumer數量不能大於Partition數量,而在RocketMQ中Consumer的數量也不能大於佇列(Queue)的數量,如果Consumer超過佇列數量,那麼多餘的Consumer將不能消費訊息。可以簡單理解為queue與consumer的關係是多對一的關係。

訊息儲存
Topic & message queue:一個分散式訊息佇列中介軟體部署好以後,可以給很多個業務提供服務,同一個業務也有不同型別的訊息要投遞,這些不同型別的訊息以不同的 Topic 名稱來區分。所以傳送和接收訊息前,先建立topic,針對某個 Topic 傳送和接收訊息。有了 Topic 以後,還需要解決效能問題 。 如果一個Topic 要傳送和接收的資料量非常大, 需要能支援增加並行處理的機器來提高處理速度,這時候一個 Topic 可以根據需求設定一個或多個 Message Queue, Message Queue 類似分割槽或 Partition 。Topic有了多個 Message Queue 後,訊息可以並行地向各個Message Queue 傳送,消費者也可以並行地從多個 Message Queue 讀取訊息並消費 。

5、物理部署結構

物理結構
RocketMQ使用Name Server叢集加Broker叢集的方式來搭建。

Name Server是一個幾乎無狀態節點,可叢集部署,節點之間無任何資訊同步。

Name Server叢集:提供topic的路由資訊,路由資訊資料儲存在記憶體中,broker會定時的傳送路由資訊到nameserver中的每一個機器,來進行更新,所以name server叢集可以簡單理解為無狀態(實際情況下可能存在每個nameserver機器上的資料有短暫的不一致現象,但是通過定時更新,大部分情況下都是一致的)。

Broker部署相對複雜,Broker分為Master與Slave,一個Master可以對應多個Slave,但是一個Slave只能對應一個Master,Master與Slave的對應關係通過指定相同的BrokerName,不同的BrokerId來定義,BrokerId為0表示Master,非0表示Slave。Master也可以部署多個。每個Broker與Name Server叢集中的所有節點建立長連線,定時註冊Topic資訊到所有Name Server。

Broker叢集:一個叢集有一個統一的名字,即brokerClusterName,預設是DefaultCluster。一個叢集下有多個master,每個master下有多個slave。master和slave算是一組,擁有相同的brokerName,不同的brokerId,master的brokerId是0,而slave則是大於0的值。master和slave之間可以進行同步複製或者是非同步複製。

Producer與Name Server叢集中的其中一個節點(隨機選擇)建立長連線,定期從Name Server取Topic路由資訊,並向提供Topic服務的Master建立長連線,且定時向Master傳送心跳。Producer完全無狀態,可叢集部署。

Producer叢集:擁有相同的ProducerGroup,一般來講,Producer不必要有叢集的概念,這裡的叢集僅僅在RocketMQ的分散式事務中有用到。

Consumer與Name Server叢集中的其中一個節點(隨機選擇)建立長連線,定期從Name Server取Topic路由資訊,並向提供Topic服務的Master、Slave建立長連線,且定時向Master、Slave傳送心跳。Consumer既可以從Master訂閱訊息,也可以從Slave訂閱訊息,訂閱規則由Broker配置決定。

consumer叢集:擁有相同的consumerGroup。

Message Filter:可以實現高階的自定義的訊息過濾。

通訊原理
分割槽與佇列的區別

為提高訊息讀寫併發能力,將一個topic訊息進行拆分,kafka稱為分割槽,rocketmq稱為佇列。

對於kafka:為了防止一個分割槽的訊息檔案過大,會拆分成一個個固定大小的檔案,所以一個分割槽就對應了一個目錄,分割槽與分割槽之間是相互隔離的。

對於RocketMQ:則是所有topic的資料混在一起進行儲存,預設超過1G的話,則重新建立一個新的檔案。訊息的寫入過程即寫入該混雜的檔案中,然後又有一個執行緒服務,在不斷的讀取分析該混雜檔案,將訊息進行分揀,然後儲存在對應佇列目錄中(儲存的是簡要資訊,如訊息在混雜檔案中的offset,訊息大小等)。所以RocketMQ需要2次尋找,第一次先找佇列中的訊息概要資訊,拿到概要資訊中的offset,根據這個offset再到混雜檔案中找到想要的訊息。而kafka則只需要直接讀取分割槽中的檔案即可得到想要的訊息。

producer端發現

Producer端如何來發現新的broker地址?

對於kafka來說:Producer端需要配置broker的列表地址,Producer也從一個broker中來更新broker列表地址(從中發現新加入的broker)。

對於RocketMQ來說:Producer端需要Name Server的列表地址,同時還可以定時從一個HTTP地址中來獲取最新的Name Server的列表地址,然後從其中的一臺Name Server來獲取全部的路由資訊,從中發現新的broker。

消費offset的儲存

對於kafka:Consumer將消費的offset定時儲存到ZooKeeper上,利用ZooKeeper保障了offset的高可用問題。

對於RocketMQ:Consumer將消費的offset定時儲存到broker所在的機器上,這個broker優先是master,如果master掛了的話,則會選擇slave來儲存,broker也是將這些offset定時重新整理到本地磁碟上,同時slave會定時的訪問master來獲取這些offset。

負載均衡

對於consumer負載均衡,在出現分割槽或者佇列增加或者減少的時候、Consumer增加或者減少的時候都會進行reblance操作。

對於RocketMQ:客戶端自己會定時對所有的topic的進行reblance操作,對於每個topic,會從broker獲取所有Consumer列表,從broker獲取佇列列表,按照負載均衡策略,計算各自負責哪些佇列。這種就要求進行負載均衡的時候,各個Consumer獲取的資料是一致的,不然不同的Consumer的reblance結果就不同。

對於kafka:kafka之前也是客戶端自己進行reblance,依靠ZooKeeper的監聽,來監聽上述2種情況的出現,一旦出現則進行reblance。現在的版本則將這個reblance操作轉移到了broker端來做,不但解決了RocketMQ上述的問題,同時減輕了客戶端的操作,客戶端更加輕量級,減少了和其它語言整合的工作量。

備註

Name Server和ZooKeeper?

Name Server和ZooKeeper的作用大致是相同的。

Name Server:從巨集觀上來看,Name Server做的東西很少,就是儲存一些執行資料,Name Server之間不互連,這就需要broker端連線所有的Name Server,執行資料的改動要傳送到每一個Name Server來保證執行資料的一致性(這個一致性確實有點弱),這樣就變成了Name Server很輕量級,但是broker端就要做更多的東西了。

ZooKeeper:broker只需要連線其中的一臺機器,執行資料分發、一致性都交給了ZooKeeper來完成。

相關文章