導(dǎo)讀
白話系列文章講述RocketMQ。因為是白話,盡量通過比較直白的方式來介紹RocketMQ,所以涉及到詳細的技術(shù)細節(jié)可能表述的不是那么嚴謹。但是不用擔心,后續(xù)會有專門的文章詳細介紹技術(shù)細節(jié)。
這篇文章介紹的是RocketMQ基本概念,分為介紹和提問兩部分,如果對概念很清楚了就不用了,閑暇無事可以看看提問。
類似介紹概念的文章網(wǎng)上比較多,希望這篇文章提問式的閱讀會讓大家對概念能有更清晰的認識。
MQ
Message Queue消息隊列,既然是隊列,就要實現(xiàn)數(shù)據(jù)結(jié)構(gòu)中隊列的基本特征,比如先進先出,入隊、出隊操作等。
RocketMQ就是把內(nèi)存中使用的那個隊列,變成一個獨立的、大家都可以用的隊列系統(tǒng)。

Topic
一個業(yè)務(wù)事件,是整個MQ領(lǐng)域最核心的概念,無論是生產(chǎn)還是消費都是針對Topic進行操作。
如果MQ是個大的隊列,只有一個隊列可以用太浪費了吧,來分一分分一分,分解成很多個小的獨立的隊列。RocketMQ變成一個管理隊列的系統(tǒng),而分解下來的若干個小的隊列通過什么來區(qū)分呢?
就是通過topic。
比如我的業(yè)務(wù)定義topic:tp_im_event。你的業(yè)務(wù)定義topic:tp_cargo_event,那就是兩個小隊列了,我的業(yè)務(wù)用我的隊列,你的項目用你的隊列。Topic就是隊列的名字。
提問:
如果不小心定義了相同的Topic名字,上線后會發(fā)生什么?
申請Topic好麻煩,所有業(yè)務(wù)都用一個Topic好了,這樣會有什么問題?
Topic名字起的越酷炫越好?

Queue
既然Topic是隊列的名字,那么queue就表示真實操作的隊列了。一開始的時候一個Topic就對應(yīng)一個queue,多好,一個是名字、一個是現(xiàn)實??墒怯弥弥捅吡?,為啥?消息操作太多了,全都懟在一個小隊列上。為了提高效率,咋整??RocketMQ是這樣做的,一個Topic綁定的是一組queue,這樣每個queue分攤部分壓力,性能就上去了。
讀隊列個數(shù):可以用來讀取數(shù)據(jù)的隊列個數(shù)
寫隊列個數(shù):可以用來寫入數(shù)據(jù)的隊列個數(shù)
queue:真實存儲數(shù)據(jù)用的隊列。
提問:
我申請了一個Topic,讀隊列設(shè)置2,寫隊列設(shè)置4有什么問題么?
我申請了一個Topic,讀隊列設(shè)置4,寫隊列設(shè)置2有什么問題么?
既然增加隊列數(shù)可以提升性能,我申請8848個隊列的Topic是不是可以達到性能的巔峰?

Message
好了,說完了隊列,我們再來說一說隊列存儲的內(nèi)容是什么?
存儲的是消息!Message!盡量小,別發(fā)個文件啊什么的大東西,后面真心扛不?。ǔ^特定大小還會報錯)

Tag
一個queue里都是消息,如何對這些消息進行歸類呢?為了進一步細化消息,有了Tag的概念。可以通過Tag對相同消息進行歸類,這樣用戶就可以只訂閱一部分的消息了(只訂閱部分Tag)
比如:有一個Topic叫做‘發(fā)貨’,下游消費者希望可以根據(jù)貨源進行不同的處理,可以通過‘tag=北京’以及‘tag=上?!瘉韰^(qū)分不同的發(fā)貨源。下游消費者,可以單獨訂閱‘上?!呢浳?,或者‘tag=上海|江蘇|浙江’來訂閱這三個地區(qū)的貨物,還可以‘tag=*’來訂閱全國的貨物。

Key
發(fā)送了某個消息,但是希望在后臺很方便的搜索到,就要通過key了??梢愿鶕?jù)key搜索到所有相關(guān)的Message??梢哉J為RocketMQ內(nèi)部維護了一個非常大的HashMap,key就是這個key,value就是Message,如果出現(xiàn)Hash沖突就用鏈表來報錯對應(yīng)關(guān)系。
提問:
每次申請Topic好煩啊,索性申請個叫tp_all的topic算了,然后內(nèi)部用tag來區(qū)分豈不是美滋滋,這樣很好吧?
我是生產(chǎn)者,我可以任意修改發(fā)送的消息體?
一個topic里面有什么tag我又不知道,索性消費所有消息,內(nèi)部判斷是不是我要的消息內(nèi)容不就好?
Producer
生產(chǎn)者:針對某一個Topic制造數(shù)據(jù),把數(shù)據(jù)塞到queue里。
簡單點:發(fā)消息的
Producer Group
管理消息的時候,我們肯定會遇見這個問題,某個消息誰發(fā)的?RocketMQ把發(fā)送者的身份抽象成了Producer Group,就是[發(fā)送組]。
簡單點:這個東西命名成項目名就行,相同Producer Group保持相同業(yè)務(wù)行為
提問:
我的項目要發(fā)送10個Topic,定義相同的Producer Group可以么?
有一個Topic,可以多個Producer Group一起生產(chǎn)么?
2臺機器有相同的Producer Group,機器1發(fā)送tp1、 機器2發(fā)送tp2這樣有問題么?
一個Topic有Producer Group:‘test_group’ 兩個項目都用了,但是A項目發(fā)送的tag叫A,B項目發(fā)送的消息Tag是B,請問有問題么??
Consumer
消費者:把queue里面的消息拿出來用
消費行為:如何處理通過Topic+Tag定位的消息
Consumer Group
重點!重點!重點!來了,直接翻譯是‘消費組’
一個RocketMQ集群是如何區(qū)分消費者是誰的呢?就是通過消費組,相同消費組的機器,MQ認為消費行為是一致的。業(yè)務(wù)上一定要保證相同消費組有相同的消費行為。對于不同的消費組名字,RocketMQ就認為是個不同消費者了。如果修改了消費組的名字,那就是新的消費者,就會按照新的消費組的消費進度處理消費。
? ? 消息那么多,項目都重啟無數(shù)次了,RocketMQ是如何記錄消息消費到什么地方了呢?
? ? 也是通過消費組,RocketMQ內(nèi)部會維護一個關(guān)系,記錄Consumer Group和消費進度之間的聯(lián)系。所以,如果把Consumer Group的名字改掉是可能重新消費之前的所有數(shù)據(jù)的(視初始消費位置而定)
提問:
兩個服務(wù),服務(wù)A和服務(wù)B,消費相同集群的相同Topic,既然服務(wù)不一樣,那么就算是定義了相同的consumer group也無所謂吧?
常見問題:消費組名字命名的不合理,上線后悄悄改回來行不行?
不小心用了別人的消費組名,悄悄改回來重新上線也沒什么問題吧?
常見問題:一個服務(wù)有消費組A消費3個Topic,有一次上線,希望消費4個Topic。對于新消費的消息希望可以灰度驗證一段時間。請問有問題么?
消息延遲/積壓
消息隊列主要的功能是模塊結(jié)偶,同步轉(zhuǎn)異步和削峰,必然會出現(xiàn)生產(chǎn)非??斓窍M慢這種事情,比如生產(chǎn)的速度是100000/s但是消費速度是1/s,這個時候就叫做消息積壓或者消費延遲(Delay)。理論上RockeMQ對于這種場景有比較好的適應(yīng)能力,原理大致這樣:正常的生產(chǎn)消費都是操作內(nèi)存數(shù)據(jù),所以比較快。但是如果積壓非常多,內(nèi)存明顯扛不住了,則降級為生產(chǎn)消費的是磁盤數(shù)據(jù),直接操作磁盤。磁盤肯定比內(nèi)存的速度慢很多啦。
這個時候整個集群的處理能力就拉低了。所以最好生產(chǎn)和消費能力不要相差太多,即便相差很多,積壓也應(yīng)該在有限的時間內(nèi)處理完畢。
目前比較容易出現(xiàn)消息積壓的情況有:
1.新消費組上線(消費歷史消息)
2.消費能力弱
3.生產(chǎn)洪峰(比如for循環(huán)發(fā)消息,job發(fā)消息)
由于RocketMQ開源版本沒有多租戶隔離,所以公共集群使用的過程中會有相互影響發(fā)生,鑒于此大家在上線前還是要合理評估自己的系統(tǒng)能力。
提問:
消費延遲太多了,業(yè)務(wù)上接受丟棄一部分消息,如何操作呢?
消息的處理線程太少了,想加大處理線程怎么辦?
自己搞個線程池處理消息是不是很贊?
InstanceName
這個概念比較尷尬。上面說的Producer Group和Consumer Group都是邏輯概念。如果需要連接多集群,就需要物理上進行區(qū)分(Instance Name)。
一個Instance Name對應(yīng)一個連接,默認的值是本機ip@進程號。連接多集群的時候務(wù)必修改這個值。
提問:
要向兩個RocketMQ集群生產(chǎn)數(shù)據(jù),只需要設(shè)置不同的Producer Group即可?
要從兩個RocketMQ集群消費數(shù)據(jù),只需要設(shè)置不同的Consumer Group即可?