一、什么是RocketMQ
RocketMQ 是一個分布式消息中間件,其具有低延遲、高性能和可靠性、萬億級容量、靈活的可擴展性特性。它主要有四部分組成,分別為 name servers,brokers,producers and consumers。
每部分都可以進行水平擴展,而不會出現單點問題。
- NameServer Cluster:名稱服務集群,提供輕量級的服務發現與路由服務,每個名稱服務器記錄了全部的 broker 的路由信息,並且提供相應的讀寫服務,支持快速存儲擴展。
- Broker Cluster:broker 集群,broker 通過提供輕量級的主題和隊列機制來維護消息存儲。它支持推和拉兩種模型,包含容錯機制(2 個副本或 3 個副本),並提供了強大的平滑峰值,提供積累數以億計的消息並保證其在原始時間順序的被消費能力。
此外,broker 也提供災難恢復、豐富的度量統計和警報機制,所有這些能力在傳統的消息傳遞系統里面都是沒有的。
- Producer Cluster:生產者集群,提供分布式部署,分布式的生產者發送消息到 broker 集群,具體選擇哪一個 broker 機器是通過一定的負載均衡策略來決定的,發送消息中支持故障快速恢復,並且具有較低的延遲時間
- Consumer Cluster:消費者集群,消費者在推和拉模型中支持分布式部署。它還支持集群消費和消息廣播。它提供了實時消息訂閱機制,可以滿足大多數消費者的需求。
broker 在啟動時候會去鏈接 NameServer,然后注冊 topic 信息到 NameServer,NameServer 維護了所有 topic 的信息和對應的 broker 路由信息,broker 與 NameServer 之間是有心跳檢查的,NameServer 發現 broker 掛了后,會從注冊信息里面刪除該 broker,這類似 zookeeper 實現的服務注冊;producer 則需要配置 nameserver 的地址,然后定時從 NameServer 獲取對應 topic 的路由信息(這個 topic 的消息應該路由到那個 broker)。
同時 producer 與 NameServer,proudcer 與 broker 有心跳檢查;同理 Consumer 需要配置 NameServer 的地址,然后定時從 NameServer 獲取對應 topic 的路由信息(應該從那個 broker 的消息隊列獲取消息),同時 Consumer 與 NameServer,Consumer 與 broker 有心跳檢查。
二、RocketMQ概念
1. MQ
Message Queue消息隊列,既然是隊列,就要實現數據結構中隊列的基本特征,比如先進先出,入隊、出隊操作等。
RocketMQ就是把內存中使用的那個隊列,變成一個獨立的、大家都可以用的隊列系統。
2. Topic
一個業務事件,是整個MQ領域最核心的概念,無論是生產還是消費都是針對Topic進行操作。
如果MQ是個大的隊列,只有一個隊列可以用太浪費了吧,來分一分,分解成很多個小的獨立的隊列。RocketMQ變成一個管理隊列的系統,而分解下來的若干個小的隊列通過什么來區分呢?
就是通過topic。
比如我的業務定義topic:tp_im_event。你的業務定義topic:tp_cargo_event,那就是兩個小隊列了,我的業務用我的隊列,你的項目用你的隊列。Topic就是隊列的名字。
既然Topic是隊列的名字,那么queue就表示真實操作的隊列了。一開始的時候一個Topic就對應一個queue,多好,一個是名字、一個是現實。可是用着用着就悲催了,為啥?消息操作太多了,全都放在一個小隊列上。為了提高效率,咋整??RocketMQ是這樣做的,一個Topic綁定的是一組queue,這樣每個queue分攤部分壓力,性能就上去了。
讀隊列個數:可以用來讀取數據的隊列個數
寫隊列個數:可以用來寫入數據的隊列個數
queue:真實存儲數據用的隊列。
4. Message
隊列存儲的是消息!Message!盡量小,別發個文件啊什么的大東西,后面真心扛不住(超過特定大小還會報錯)。
5. Tag
一個queue里都是消息,如何對這些消息進行歸類呢?為了進一步細化消息,有了Tag的概念。可以通過Tag對相同消息進行歸類,這樣用戶就可以只訂閱一部分的消息了(只訂閱部分Tag)
比如:有一個Topic叫做‘發貨’,下游消費者希望可以根據貨源進行不同的處理,可以通過‘tag=北京’以及‘tag=上海’來區分不同的發貨源。下游消費者,可以單獨訂閱‘上海’的貨物,或者‘tag=上海|江蘇|浙江’來訂閱這三個地區的貨物,還可以‘tag=*’來訂閱全國的貨物。
6. Key
發送了某個消息,但是希望在后台很方便的搜索到,就要通過key了。可以根據key搜索到所有相關的Message。可以認為RocketMQ內部維護了一個非常大的HashMap,key就是這個key,value就是Message,如果出現Hash沖突就用鏈表來報錯對應關系。
7. Producer
生產者:針對某一個Topic制造數據,把數據塞到queue里。(發消息的)
8. Producer Group
管理消息的時候,我們肯定會遇見這個問題,某個消息誰發的?RocketMQ把發送者的身份抽象成了Producer Group,就是[發送組]。
簡單點:這個東西命名成項目名就行,相同Producer Group保持相同業務行為。
9. Consumer
消費者:把queue里面的消息拿出來用
消費行為:如何處理通過Topic+Tag定位的消息。
10. Consumer Group
一個RocketMQ集群是如何區分消費者是誰的呢?就是通過消費組,相同消費組的機器,MQ認為消費行為是一致的。業務上一定要保證相同消費組有相同的消費行為。對於不同的消費組名字,RocketMQ就認為是個不同消費者了。如果修改了消費組的名字,那就是新的消費者,就會按照新的消費組的消費進度處理消費。
消息那么多,項目都重啟無數次了,RocketMQ是如何記錄消息消費到什么地方了呢?
也是通過消費組,RocketMQ內部會維護一個關系,記錄Consumer Group和消費進度之間的聯系。所以,如果把Consumer Group的名字改掉是可能重新消費之前的所有數據的(視初始消費位置而定)
11. 消息延遲/積壓
消息隊列主要的功能是模塊結偶,同步轉異步和削峰,必然會出現生產非常快但是消費慢這種事情,比如生產的速度是100000/s但是消費速度是1/s,這個時候就叫做消息積壓或者消費延遲(Delay)。理論上RockeMQ對於這種場景有比較好的適應能力,原理大致這樣:正常的生產消費都是操作內存數據,所以比較快。但是如果積壓非常多,內存明顯扛不住了,則降級為生產消費的是磁盤數據,直接操作磁盤。磁盤肯定比內存的速度慢很多啦。
這個時候整個集群的處理能力就拉低了。所以最好生產和消費能力不要相差太多,即便相差很多,積壓也應該在有限的時間內處理完畢。
目前比較容易出現消息積壓的情況有:
- 新消費組上線(消費歷史消息)
- 消費能力弱
- 生產洪峰(比如for循環發消息,job發消息)
由於RocketMQ開源版本沒有多租戶隔離,所以公共集群使用的過程中會有相互影響發生,鑒於此大家在上線前還是要合理評估自己的系統能力。
12. InstanceName
上面說的Producer Group和Consumer Group都是邏輯概念。如果需要連接多集群,就需要物理上進行區分(Instance Name)。
一個Instance Name對應一個連接,默認的值是本機ip@進程號。連接多集群的時候務必修改這個值。
三、MQ的使用場景
消息隊列中間件是分布式系統中重要的組件,主要解決應用解耦,異步消息,流量削鋒等問題,實現高性能,高可用,可伸縮和最終一致性架構。
1. 異步處理
場景說明:用戶注冊后,需要發注冊郵件和注冊短信。傳統的做法有兩種 (1) 串行的方式;(2) 並行方式
注冊郵件,發送短信寫入消息隊列后,直接返回,縮短了響應時間,提高了吞吐量。
2. 應用解耦
場景說明:用戶下單后,訂單系統需要通知庫存系統。傳統的做法是,訂單系統調用庫存系統的接口。
傳統模式的缺點:假如庫存系統無法訪問,則訂單減庫存將失敗,從而導致訂單失敗,訂單系統與庫存系統耦合。
引入消息隊列后,
訂單系統:用戶下單后,訂單系統完成持久化處理,將消息寫入消息隊列,返回用戶訂單下單成功。
庫存系統:訂閱下單的消息,采用拉/推的方式,獲取下單信息,庫存系統根據下單信息,進行庫存操作。
假如:在下單時庫存系統不能正常使用。也不影響正常下單,因為下單后,訂單系統寫入消息隊列就不再關心其他的后續操作了。實現訂單系統與庫存系統的應用解耦。
3. 流量削峰
流量削鋒也是消息隊列中的常用場景,一般在秒殺或團搶活動中使用廣泛。
應用場景:秒殺活動,一般會因為流量過大,導致流量暴增,應用掛掉。為解決這個問題,一般需要在應用前端加入消息隊列。
a、可以控制活動的人數。
b、可以緩解短時間內高流量壓垮應用。
用戶的請求,服務器接收后,首先寫入消息隊列。假如消息隊列長度超過最大數量,則直接拋棄用戶請求或跳轉到錯誤頁面。
秒殺業務根據消息隊列中的請求信息,再做后續處理
4. 日志處理
日志處理是指將消息隊列用在日志處理中,比如Kafka的應用,解決大量日志傳輸的問題。
日志采集客戶端,負責日志數據采集,定時寫受寫入Kafka隊列。
Kafka消息隊列,負責日志數據的接收,存儲和轉發。
日志處理應用:訂閱並消費kafka隊列中的日志數據 。
5. 消息通訊
消息通訊是指,消息隊列一般都內置了高效的通信機制,因此也可以用在純的消息通訊。比如實現點對點消息隊列,或者聊天室等。