metaq與rocketmq區(qū)別
metaq屬于rocketmq阿里內(nèi)部定制化版本偎窘,整體基本一致
使用場景
1.削峰填谷:大促物流訂單創(chuàng)建
2.異步解耦:應(yīng)用解耦重要手段
3.異步數(shù)據(jù)同步:精衛(wèi)binlog同步
4.流計算:Flink等計算平臺
metaq是什么
是一個隊(duì)列模型的消息中間件凿傅,具有高性能泊交、高可靠、高實(shí)時宏浩、分布式特點(diǎn)知残。
Producer、Consumer比庄、隊(duì)列都可以分布式求妹。
Producer 向一些隊(duì)列輪流發(fā)送消息,隊(duì)列集合稱為 Topic印蔗,Consumer 如果做廣播消費(fèi)扒最,則一個consumer實(shí)例消費(fèi)這個 Topic 對應(yīng)的所有隊(duì)列丑勤,如果做集群消費(fèi)华嘹,則多個 Consumer 實(shí)例平均消費(fèi)這個 topic 對應(yīng)的隊(duì)列集合。
能夠保證嚴(yán)格的消息順序法竞。
提供豐富的消息拉取模式耙厚。
高效的訂閱者水平擴(kuò)展能力。
實(shí)時的消息訂閱機(jī)制岔霸。
億級消息堆積能力薛躬。
基本概念
消息:信息傳遞的載體,數(shù)據(jù)傳遞的基本單位
隊(duì)列:先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu)呆细,消息收發(fā)的負(fù)載均衡都是基于隊(duì)列的維度實(shí)現(xiàn)
Topic:消息主題型宝,一級消息類型,通過topic對消息進(jìn)行分類,每個topic下可以區(qū)分不同tag趴酣,同一個Topic下的數(shù)據(jù)梨树,會分片保存到不同的Broker上,而每一個分片單位岖寞,就叫做MessageQueue抡四。
消費(fèi)組:消費(fèi)分組的標(biāo)識,即sdk中的groupName仗谆,也是文檔中提交到的consumerID指巡,接收并消費(fèi)一類信息
物理結(jié)構(gòu)
NameServer:用于服務(wù)發(fā)現(xiàn),提供名稱服務(wù)隶垮,NameServer是一個幾乎無狀態(tài)節(jié)點(diǎn)藻雪,可集群部署,節(jié)點(diǎn)之間無任何信息同步(維護(hù)topic與broker關(guān)系岁疼,producer/consumer通過topic從nameserver獲取broker)
Broker:MetaQ的服務(wù)器阔涉,負(fù)責(zé)消息中轉(zhuǎn),主要職責(zé)是存儲捷绒、轉(zhuǎn)發(fā)消息瑰排。 部署相對復(fù)雜,Broker 分為 Master 與 Slave暖侨,一個 Master 可以對應(yīng)多個 Slave椭住,但是一個 Slave 只能對應(yīng)一個 Master,Master 與 Slave 的對應(yīng)關(guān)系通過指定相同的 BrokerName字逗,不同的 BrokerId 來定義京郑。BrokerId為 0 表示 Master,非 0 表示 Slave葫掉。Master 也可以部署多個些举。每個 Broker 與 Name Server 集群中的所有節(jié)點(diǎn)建立長連接,定時注冊 Topic 信息到所有 NameServer俭厚。
Producer:消息生產(chǎn)者户魏,負(fù)責(zé)產(chǎn)生消息,一般是業(yè)務(wù)系統(tǒng)挪挤。 Producer 與 Name Server 集群中的其中一個節(jié)點(diǎn)(隨機(jī)選擇)建立長連接叼丑,定期從 Name Server 取 Topic 路由信息,并向提供 Topic 服務(wù)的 Master 建立長連接扛门,且定時向 Master 發(fā)送心跳鸠信。Producer 完全無狀態(tài),可集群部署论寨。
Consumer:消息消費(fèi)者星立,負(fù)責(zé)消費(fèi)消息爽茴,一般是后臺系統(tǒng)負(fù)責(zé)異步消費(fèi)。Consumer 與 Name Server 集群中的其中一個節(jié)點(diǎn)(隨機(jī)選擇)建立長連接绰垂,定期從 Name Server 取 Topic 路由信息闹啦,并向提供 Topic 服務(wù)的 Master、Slave 建立長連接辕坝,且定時向 Master窍奋、Slave 發(fā)送心跳。Consumer既可以從 Master 訂閱消息酱畅,也可以從 Slave 訂閱消息琳袄,訂閱規(guī)則由 Broker 配置決定。
Broker:MetaQ的服務(wù)器纺酸,負(fù)責(zé)消息中轉(zhuǎn)窖逗,主要職責(zé)是存儲、轉(zhuǎn)發(fā)消息餐蔬。 部署相對復(fù)雜碎紊,Broker 分為 Master 與 Slave,一個 Master 可以對應(yīng)多個 Slave樊诺,但是一個 Slave 只能對應(yīng)一個 Master仗考,Master 與 Slave 的對應(yīng)關(guān)系通過指定相同的 BrokerName,不同的 BrokerId 來定義词爬。BrokerId為 0 表示 Master秃嗜,非 0 表示 Slave。Master 也可以部署多個顿膨。每個 Broker 與 Name Server 集群中的所有節(jié)點(diǎn)建立長連接锅锨,定時注冊 Topic 信息到所有 NameServer。
Broker選舉
集群啟動時恋沃,三個節(jié)點(diǎn)都是follower必搞,發(fā)起投票后,三個節(jié)點(diǎn)都會給自己投票囊咏。這樣一輪投票下來恕洲,三個節(jié)點(diǎn)的term都是1,是一樣的匆笤,這樣是選舉不出Leader的研侣。
當(dāng)一輪投票選舉不出Leader后谱邪,三個節(jié)點(diǎn)會進(jìn)入隨機(jī)休眠炮捧,例如A休眠1秒,B休眠3秒惦银,C休眠2秒咆课。
一秒后末誓,A節(jié)點(diǎn)醒來,會把自己的term加一票书蚪,投為2喇澡。然后2秒時,C節(jié)點(diǎn)醒來殊校,發(fā)現(xiàn)A的term已經(jīng)是2晴玖,比自己的1大,就會承認(rèn)A是Leader为流,把自己的term也更新為2呕屎。實(shí)際上這個時候,A已經(jīng)獲得了集群中的多數(shù)票敬察,2票秀睛,A就會被選舉成Leader。這樣莲祸,一般經(jīng)過很短的幾輪選舉蹂安,就會選舉出一個Leader來。
到3秒時锐帜,B節(jié)點(diǎn)會醒來田盈,他也同樣會承認(rèn)A的term最大,他是Leader缴阎,自己的term也會更新為2缠黍。這樣集群中的所有Candidate就都確定成了leader和follower.
然后在一個任期內(nèi),A會不斷發(fā)心跳給另外兩個節(jié)點(diǎn)药蜻。當(dāng)A掛了后瓷式,另外的節(jié)點(diǎn)沒有收到A的心跳,就會都轉(zhuǎn)化成Candidate狀態(tài)语泽,重新發(fā)起選舉贸典。
存儲架構(gòu)
CommitLog:真正存儲消息,以物理文件存儲踱卵,每個Broker上的CommitLog被該機(jī)器所有的Queue共享廊驼。寫滿一個G之后寫下一個G,完全順序?qū)懲锷埃S機(jī)讀
ConsumerQueue:消費(fèi)者使用的邏輯隊(duì)列妒挎,每個Topic下的每個Queue都有一個對應(yīng)的ConsumeQueue文件,文件地址在$home/store/consumequeue/{topicName}/{queueId}
IndexFile:查詢用西饵,基于物理磁盤文件實(shí)現(xiàn) hash 索引酝掩;
過程:數(shù)據(jù)寫到commitlog后異步構(gòu)建consumerqueue文件和 IndexFile(索引文件)數(shù)據(jù);
consumerqueue 記錄當(dāng)前在commitlog的位移與長度眷柔,生成一個定長的20字節(jié)的consumerqueue單個文件(1.這個索引對應(yīng)的消息內(nèi)容存在commitlog的哪個offset期虾,2.記錄消息大小原朝,3.記錄消息tag hashcode)
IndexFile 只是為了消息查詢提供了一種通過 key 或時間區(qū)間來查詢消息的方法(也是根據(jù)存儲的commitlog offset去commitlog查找消息體),且這種通過 IndexFile 來查找消息的方法不影響發(fā)送與消費(fèi)消息的主流程镶苞,文件地址$home/store/index/{fileName}喳坠;
分區(qū)模型
topicA在broker1 和 broker2分別有topicA_1分區(qū), topicA_2分區(qū);topicA_3 分區(qū)茂蚓,topicA_4 分區(qū)壕鹉。每個分區(qū)里存是的commit log文件的索引信息。
消費(fèi)關(guān)系
經(jīng)典的Producer聋涨、Broker御板、Consumer消費(fèi)模型,不同類型的消息以不同的Topic來區(qū)分牛郑。
為了解決性能問題怠肋,一個Topic可以根據(jù)需求設(shè)置一個或多個消息隊(duì)列,Message Quene類似數(shù)據(jù)庫中的分區(qū)淹朋。Producer按照負(fù)載均衡策略將消息發(fā)送至Topic里不同的消息隊(duì)列中笙各,Consumer可以從隊(duì)列中取出消息并發(fā)的去消費(fèi)。
讀寫模型
- 隨機(jī)讀础芍,盡可能讓讀命中 PAGECACHE杈抢,減少 IO 讀操作,所以內(nèi)存越大越好仑性。如果系統(tǒng)中堆積的消息過多惶楼,讀數(shù)據(jù)要訪問磁盤會不會由于隨機(jī)讀導(dǎo)致系統(tǒng)性能急劇下降,答案是否定的诊杆。
訪問 PAGECACHE 時歼捐,即使只訪問 1k 的消息,系統(tǒng)也會提前預(yù)讀出更多數(shù)據(jù),在下次讀時曙聂,就可能命
中內(nèi)存。 - 隨機(jī)訪問 Commit Log 磁盤數(shù)據(jù),系統(tǒng) IO 調(diào)度算法設(shè)置為 NOOP 方式,會在一定程度上將完全的隨機(jī)
讀變成順序跳躍方式,而順序跳躍方式讀較完全的隨機(jī)讀性能會高 5 倍以上寸士。 - 由于 Consume Queue 存儲數(shù)據(jù)量極少婶博,而且是順序讀挠轴,在 PAGECACHE 預(yù)讀作用下委煤,Consume Queue 的讀性能幾乎與內(nèi)存一致,即使堆積情況下院峡。所以可認(rèn)為 Consume Queue 完全不會阻礙讀性能盹牧。
DirectMemory模式只有在淘寶雙十一大促開啟,平時不開啟睛榄,這里犧牲了1ms左右的讀數(shù)據(jù)(DirectMemory異步寫入PageCache咧欣,但是直接從PageCache讀)
順序消息
● 順序發(fā)布:對于指定的一個 Topic轨帜,客戶端將按照一定的先后順序進(jìn)行發(fā)送消息魄咕。
● 順序消費(fèi):對于指定的一個 Topic,按照一定的先后順序進(jìn)行接收消息蚌父,即先發(fā)送的消息一定會先被客戶端接收到哮兰。
全局順序:對于指定的一個 Topic,所有消息按照嚴(yán)格的先入先出(FIFO)的順序進(jìn)行發(fā)布和消費(fèi)苟弛。
分區(qū)順序:對于指定的一個 Topic喝滞,所有消息根據(jù) sharding key 進(jìn)行區(qū)塊分區(qū)。同一個分區(qū)內(nèi)的消息按照嚴(yán)格的 FIFO 順序進(jìn)行發(fā)布和消費(fèi)膏秫。Sharding key 是順序消息中用來區(qū)分不同分區(qū)的關(guān)鍵字段右遭,和普通消息的 Key 是完全不同的概念。(Sharding key如訂單,根據(jù)訂單進(jìn)行分區(qū))
事務(wù)消息
發(fā)送事務(wù)消息基本流程
發(fā)送事務(wù)消息是一個二階段過程窘哈,類似于數(shù)據(jù)庫的二階段提交吹榴。其中,包括兩個過程:
- 發(fā)送Half消息滚婉。應(yīng)用發(fā)送Half消息到Broker图筹。消費(fèi)方此時不能拉取到消息,需要等待后續(xù)Half消息狀態(tài)满哪。(Half就是將該消息轉(zhuǎn)存到另一個topic中RMQ_SYS_TRANS_HALF_TOPIC)
- 發(fā)送事務(wù)狀態(tài)婿斥。應(yīng)用根據(jù)自身的事務(wù)執(zhí)行情況劝篷,對Half消息發(fā)送事務(wù)狀態(tài):Commit哨鸭、Rollback、UNKNOW(當(dāng)前無法判決事務(wù)狀態(tài)娇妓,如事務(wù)無法及時完成)像鸡。(事務(wù)檢查通過,將該消息存到目標(biāo)topic中)
● Commit哈恰。訂閱方就可以從Broker拉取消息消費(fèi)只估;
● Rollback。Broker會“刪除”(標(biāo)記刪除)此消息着绷,訂閱者無法消費(fèi)此消息蛔钙;
● UNKNOW。當(dāng)前Half消息不處理荠医,Broker會定期(時間間隔最長約30秒(有一定浮動吁脱,可能會根據(jù)情況調(diào)整))發(fā)送Check到發(fā)送方應(yīng)用上隨機(jī)一臺機(jī)器(配置相同producerGroup的任意機(jī)器),檢查對應(yīng)的事務(wù)狀態(tài)(默認(rèn)最多檢查10次彬向,直到Commit或者Rollback兼贡,超過就當(dāng)作Rollback處理)。如果執(zhí)行事務(wù)過程中拋出異常 或者 超出時間限制(默認(rèn)6s)娃胆,也會當(dāng)UNKNOW處理遍希。
注:如果對某Half消息發(fā)送Rollback后,后續(xù)對相同Half消息發(fā)送Commit里烦,只要Half消息仍然保存在磁盤上(數(shù)小時到數(shù)十小時)凿蒜,消費(fèi)方也可以拉取到消息消費(fèi)(例如:本地事務(wù)執(zhí)行時間(executeLocalTransactionBranch執(zhí)行時間)過長,此時Broker會主動check(調(diào)用checkLocalTransactionState)胁黑,此時checkLocalTransactionState由于數(shù)據(jù)庫缺失記錄返回Rollback篙程,但最后本地事務(wù)executeLocalTransactionBranch執(zhí)行成功,對同一個事務(wù)進(jìn)行Commit會成功)别厘。反過來則不成立虱饿,即只要Commit,消費(fèi)方就會消費(fèi)到消息,Rollback也不能改變狀態(tài)氮发。這是由于Rollback是標(biāo)記刪除的實(shí)現(xiàn)導(dǎo)致的渴肉。
發(fā)送事務(wù)狀態(tài)-executeLocalTransactionBranch-對應(yīng)上圖步驟2
(發(fā)送Half消息成功后,會立即調(diào)用這里判斷當(dāng)前的事務(wù)狀態(tài)爽冕。這里也可以寫業(yè)務(wù)操作事務(wù)的代碼--不建議)
主動check-需要監(jiān)聽事件-checkLocalTransactionState-對應(yīng)上圖步驟5
延遲消息與定時消息
類似事務(wù)消息仇祭,將消息hold住(修改topic和隊(duì)列信息放入到commitlog中)颈畸,將其放入延遲隊(duì)列中乌奇,定時輪訓(xùn)延遲隊(duì)列,到時間將消息拉出來正式放到commitlog中
死信隊(duì)列
當(dāng)一條消息消費(fèi)失敗眯娱,RocketMQ就會自動進(jìn)行消息重試礁苗。而如果消息超過最大重試次數(shù),RocketMQ就會認(rèn)為這個消息有問題徙缴。但是此時试伙,RocketMQ不會立刻將這個有問題的消息丟棄,而會將其發(fā)送到這個消費(fèi)者組對應(yīng)的一種特殊隊(duì)列:死信隊(duì)列。
通常,一條消息進(jìn)入了死信隊(duì)列浅萧,意味著消息在消費(fèi)處理的過程中出現(xiàn)了比較嚴(yán)重的錯誤,并且無法自行恢復(fù)蚤蔓。此時,一般需要人工去查看死信隊(duì)列中的消息糊余,對錯誤原因進(jìn)行排查秀又。然后對死信消息進(jìn)行處理,比如轉(zhuǎn)發(fā)到正常的Topic重新進(jìn)行消費(fèi)啄刹,或者丟棄涮坐。
實(shí)踐問題
RocketMQ 的消息機(jī)制
消息模型:使用標(biāo)準(zhǔn)的發(fā)布-訂閱模型,在 RocketMQ 中也有隊(duì)列(queue)這個概念誓军;
消費(fèi)機(jī)制
- 通過“請求-確認(rèn)”機(jī)制保證消息傳遞可靠:在生產(chǎn)端袱讹,生產(chǎn)者先將消息發(fā)送給服務(wù)端,也就是 Broker昵时,服務(wù)端在收到消息并將消息寫入主題或者隊(duì)列中后捷雕,會給生產(chǎn)者發(fā)送確認(rèn)的響應(yīng),如果生產(chǎn)者沒有收到服務(wù)端的確認(rèn)或者收到失敗的響應(yīng)壹甥,則會重新發(fā)送消息救巷;在消費(fèi)端,消費(fèi)者在收到消息并完成自己的消費(fèi)業(yè)務(wù)邏輯后句柠,也會給服務(wù)端發(fā)送消費(fèi)成功的確認(rèn)浦译,服務(wù)端只有收到消費(fèi)確認(rèn)后棒假,才認(rèn)為一條消息被成功消費(fèi),否則它會給消費(fèi)者重新發(fā)送這條消息精盅,直到收到對應(yīng)的消費(fèi)成功確認(rèn)帽哑;
- 利用“多隊(duì)列”提升消費(fèi)性能:每個主題包含多個隊(duì)列,通過多個隊(duì)列來實(shí)現(xiàn)多實(shí)例并行生產(chǎn)和消費(fèi)任意時刻叹俏,允許多個消費(fèi)者進(jìn)行消費(fèi)妻枕;
- 每個主題包含多個隊(duì)列,通過多個隊(duì)列來實(shí)現(xiàn)多實(shí)例并行生產(chǎn)和消費(fèi)粘驰;
- 生產(chǎn)者會往所有隊(duì)列發(fā)消息屡谐,但一條消息只會往其中的某個隊(duì)列發(fā)送一次;
- 一個消費(fèi)組蝌数,在每個隊(duì)列上只能串行消費(fèi)愕掏,多個隊(duì)列加一起就是并行消費(fèi),并行度就是隊(duì)列數(shù)量籽前;
- 一個消費(fèi)組在一個隊(duì)列上維護(hù)一個消費(fèi)位置(offset)亭珍,作為這個消費(fèi)組在這個隊(duì)列上消費(fèi)記錄敷钾;
- 訂閱者是通過消費(fèi)組(Consumer Group)來體現(xiàn)的枝哄,每個消費(fèi)組都消費(fèi)主題中一份完整的消息,一條消息被 Consumer Group1 消費(fèi)過阻荒,也可被 Consumer Group2 消費(fèi)挠锥;
- 消費(fèi)組中包含多個消費(fèi)者,同一個組內(nèi)的消費(fèi)者是競爭關(guān)系侨赡,每個消費(fèi)者負(fù)責(zé)消費(fèi)組內(nèi)的一部分消息蓖租,如果一條消息被消費(fèi)者 Consumer1 消費(fèi)了,那同組的其他消費(fèi)者就不會再收到這條消息羊壹;
- RocketMQ 只在隊(duì)列上保證消息的有序性蓖宦,主題層面無法保證消息的嚴(yán)格順序;
消息如何發(fā)送到指定隊(duì)列
https://blog.csdn.net/lgxzzz/article/details/119617998
順序隊(duì)列怎么實(shí)現(xiàn)
1.rocketmq自帶
2.其他mq如果沒有順序隊(duì)列
1.拆分多個隊(duì)列油猫,每個隊(duì)列對應(yīng)一個消費(fèi)者
2.將要順序操作的消息存到一個內(nèi)存隊(duì)列中稠茂,然后統(tǒng)一發(fā)送消息,在消費(fèi)端順序處理情妖,保證事務(wù)
3.通過消息體加狀態(tài)機(jī)/order排序睬关,由業(yè)務(wù)判斷處理
rocketmq順序消息是怎么實(shí)現(xiàn)的
rocketMQ實(shí)現(xiàn)嚴(yán)格的順序消費(fèi)采用的方法是:保證同一個order的消息發(fā)送到同一個隊(duì)列
發(fā)送者發(fā)送消息時,會通過MessageQueue輪詢的方式保證消息盡量均勻的分布到所有的MessageQueue上毡证,而消費(fèi)者也就同樣需要從多個MessageQueue上消費(fèi)消息电爹。而MessageQueue是RocketMQ存儲消息的最小單元,他們之間的消息都是互相隔離的料睛,在這種情況下丐箩,是無法保證消息全局有序的摇邦。
而對于局部有序的要求,只需要將有序的一組消息都存入同一個MessageQueue里屎勘,這樣MessageQueue的FIFO設(shè)計天生就可以保證這一組消息的有序涎嚼。RocketMQ中,可以在發(fā)送者發(fā)送消息時指定一個MessageSelector對象挑秉,讓這個對象來決定消息發(fā)入哪一個MessageQueue法梯。這樣就可以保證一組有序的消息能夠發(fā)到同一個MessageQueue里。
MessageQueueSelector實(shí)現(xiàn)
SendResult sendResult = producer.send(msg, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
Long id = (Long) arg;
long index = id % mqs.size();
return mqs.get((int)index);
}
有序消費(fèi)(MessageListenerOrderly)
隊(duì)列分配: 有序消費(fèi)要求消息按照順序被處理犀概,因此 RocketMQ 會將同一個隊(duì)列的消息分配給同一個消費(fèi)者線程立哑。這確保了同一個隊(duì)列內(nèi)的消息是有序的。
消息鎖定: 在有序消費(fèi)中姻灶,為了保證消息的順序性铛绰,通常會對隊(duì)列進(jìn)行鎖定,即同一時刻只有一個消費(fèi)者線程能夠處理該隊(duì)列的消息产喉。
并發(fā)消費(fèi)(MessageListenerConcurrently)
隊(duì)列分配: 并發(fā)消費(fèi)允許多個消息隊(duì)列被并行處理捂掰。RocketMQ 會將消息盡可能平均地分配給不同的消費(fèi)者線程,以提高并發(fā)性能曾沈。
無鎖定: 由于并發(fā)消費(fèi)不要求嚴(yán)格的順序性这嚣,不會對隊(duì)列進(jìn)行鎖定。多個消費(fèi)者線程可以并行處理不同隊(duì)列的消息塞俱,提高整體的消費(fèi)吞吐量姐帚。
消息是如何亂序的
生產(chǎn)者生產(chǎn)了 2 條消息 m1、m2障涯,如果要保證 m1 先于 m2 被消費(fèi)
m1罐旗、m2 發(fā)送到 MQ 的不同 server,m1 到達(dá)的比 m2 晚唯蝶;
m1九秀、m2 發(fā)送到 MQ 的同一 server,保證 m1 先于 m2 到達(dá) server粘我,根據(jù)先到達(dá)先消費(fèi)的原則鼓蜒,理論上,m1 會先于 m2 被消費(fèi)涂滴,但 m1 和 m2 被發(fā)送到不同的消費(fèi)者友酱,1)m1 到達(dá)消費(fèi)者的時間要比m2 晚,2)m1 和 m2 同時到達(dá)柔纵,但兩個消費(fèi)端的負(fù)載情況不一樣缔杉;
解決:簡單可行的辦法是保證生產(chǎn)者 - MQServer - 消費(fèi)者是一對一對一的關(guān)系,但為了保證消息集群的高容錯性和高吞吐量搁料,需要從業(yè)務(wù)層面來保證消息的順序而不僅僅依賴于消息系統(tǒng)或详,RocketMQ 通過輪詢所有隊(duì)列的方式來確定消息被發(fā)送到哪一個隊(duì)列(負(fù)載均衡策略)系羞,在獲取到路由信息以后,會根據(jù)MessageQueueSelector 實(shí)現(xiàn)的算法來選擇一個隊(duì)列霸琴,同一個 OrderId(訂單 id)獲取到的肯定是同一個隊(duì)列椒振;
補(bǔ)充:其 ta 保證順序消息 & 消息去重的方法
1.使用版本號:下游每次處理消息時,只接受比當(dāng)前版本大的消息梧乘,但也引入了問題澎迎,一是發(fā)送方必須要求消息帶上業(yè)務(wù)版本號,二是下游必須存儲消息的版本號且嚴(yán)格保證順序选调,整體實(shí)現(xiàn)需要較高的成本夹供;
2.使用狀態(tài)機(jī):業(yè)務(wù)方只需要自己維護(hù)一個狀態(tài)機(jī),定義各種狀態(tài)的流轉(zhuǎn)關(guān)系仁堪,例如哮洽,”下線”狀態(tài)只允許接收”上線”消息,“上線”狀態(tài)只能接收“下線消息”弦聂,如果上線收到上線消息鸟辅,或者下線收到下線消息,在消息不丟失和上游業(yè)務(wù)正確的前提下莺葫,要么是消息發(fā)重了匪凉,要么是順序到達(dá)反了,這時消費(fèi)者只需要把“我不能處理這個消息”告訴投遞者徙融,要求投遞者過一段時間重發(fā)即可洒缀,而且重發(fā)有一定要有次數(shù)限制瑰谜,以避免死循環(huán)欺冀;
rocketmq消費(fèi)模式
推/拉消息模式
一是消息中間件進(jìn)行數(shù)據(jù)消息推送,二是消費(fèi)者從消息中間件拉取數(shù)據(jù)消息萨脑;
push 方式:有消息就及時推給消費(fèi)者隐轩,延遲小,幾乎可以做到實(shí)時
pull 方式:消費(fèi)者可以根據(jù)自己能力拉取消息處理
rocketmq都支持
rocketmq消費(fèi)慢-批量消費(fèi)
當(dāng)rocketmq消費(fèi)較慢渤早,即使加機(jī)器改善也一般职车,此時可以使用批量消費(fèi),一次性消費(fèi)多條消息鹊杖;
通過消費(fèi)者開啟多線程消費(fèi)悴灵,然后統(tǒng)一CountDownLatch等待消費(fèi)后給ack;這里如果一次性消費(fèi)10條數(shù)據(jù)骂蓖,其中1條失敗了积瞒,整體返回失敗,然后重試登下,重試時一定要做好冪等茫孔,將成功的9條數(shù)據(jù)給冪等掉叮喳,正常處理失敗的;如果批量消費(fèi)大量失敗缰贝,且并發(fā)較高馍悟,可能會堆積消息,這里抉擇要注意剩晴。
消息如何保證消息不丟失
事務(wù)保證生產(chǎn)者確認(rèn)
消費(fèi)者同步ack
消費(fèi)冪等
發(fā)送時消息重復(fù)【消息 Message ID 不同】:MQ Producer 發(fā)送消息場景下锣咒,消息已成功發(fā)送到服務(wù)端并完成持久化,此時網(wǎng)絡(luò)閃斷或者客戶端宕機(jī)導(dǎo)致服務(wù)端應(yīng)答給客戶端失敗赞弥。如果此時 MQ Producer 意識到消息發(fā)送失敗并嘗試再次發(fā)送消息宠哄,MQ 消費(fèi)者后續(xù)會收到兩條內(nèi)容相同但是 Message ID 不同的消息。
投遞時消息重復(fù)【消息 Message ID 相同】嗤攻;MQ Consumer 消費(fèi)消息場景下毛嫉,消息已投遞到消費(fèi)者并完成業(yè)務(wù)處理,當(dāng)客戶端給服務(wù)端反饋應(yīng)答的時候網(wǎng)絡(luò)閃斷妇菱。為了保證消息至少被消費(fèi)一次承粤,MQ 服務(wù)端將在網(wǎng)絡(luò)恢復(fù)后再次嘗試投遞之前已被處理過的消息,MQ 消費(fèi)者后續(xù)會收到兩條內(nèi)容相同并且 Message ID 也相同的消息闯团。
基于上述第一種原因辛臊,內(nèi)容相同的消息 Message ID 可能會不同,真正安全的冪等處理房交,不建議以 Message ID 作為處理依據(jù)彻舰。最好的方式是以業(yè)務(wù)唯一標(biāo)識作為冪等處理的關(guān)鍵依據(jù),而業(yè)務(wù)的唯一標(biāo)識可以通過消息 Key 進(jìn)行設(shè)置
消息堆積(排除topic未配置等情況-真實(shí)消息堆積)
- 消費(fèi)正常候味,發(fā)送方量級增大超出消費(fèi)上限刃唤,出現(xiàn)堆積(此種較為少見)
- 消費(fèi)變慢,出現(xiàn)堆積(最為常見白群,通過鷹眼或者單機(jī)視角jstack尚胞、arthas、排查RT是否增長)
- 順序消費(fèi)帜慢,某個隊(duì)列某條數(shù)據(jù)消費(fèi)失敗笼裳,導(dǎo)致當(dāng)前隊(duì)列block,堆積增大(需要解決消費(fèi)失敗問題)
消費(fèi)變慢粱玲,簡單排查是由于消費(fèi)者耗時過高導(dǎo)致躬柬,還是消費(fèi)者處理異常導(dǎo)致;
解決方案
如果是耗時過高抽减,先增加機(jī)器允青,讓消息快速消費(fèi)掉,后續(xù)優(yōu)化性能
如果是處理異常胯甩,能快速解決就快速解決昧廷,不能快速解決要想辦法將數(shù)據(jù)消費(fèi)掉堪嫂,
可以通過簡單改造落庫數(shù)據(jù),然后慢慢解決木柬,后續(xù)重新消費(fèi)皆串;也可以改造成轉(zhuǎn)發(fā)到一個新的topic上,最終目標(biāo)是將積壓的消息消費(fèi)一部分眉枕;然后解決問題修復(fù)恶复;
為什么RocketMQ不使用Zookeeper作為注冊中心呢?
zp屬于cp速挑,也就是選擇了一致性谤牡,在選舉階段導(dǎo)致服務(wù)不可用,首先rocketmq是不會接受這一點(diǎn)的姥宝,rocketmq采用nameServer弱依賴注冊中心翅萤,即從nameServer獲取broker地址后緩存到生產(chǎn)者/消費(fèi)者本地保持長連接,這樣即使nameServer集群掛了腊满,也不會影響消息的生產(chǎn)與消費(fèi)套么;
Broker主從模式選擇
單Master模式
broker重啟服務(wù)不可用,不推薦
多Master模式
單臺機(jī)器宕機(jī)至拉起服務(wù)期間未消費(fèi)消息不可消費(fèi)碳蛋,實(shí)時性有影響胚泌;
多Master多Slave模式-異步復(fù)制
異步復(fù)制方式,主備有短暫消息延遲(毫秒級)肃弟,Master宕機(jī)可能丟失少量未同步到slave的消息
多Master多Slave模式-同步雙寫
HA采用同步雙寫方式玷室,即只有主備都寫成功,才向應(yīng)用返回成功笤受,性能比異步差10%左右
Master和Slave之間是怎么同步數(shù)據(jù)的呢穷缤?
消息在master和slave之間的同步是根據(jù)raft協(xié)議來進(jìn)行的:
1.在broker收到消息后,會被標(biāo)記為uncommitted狀態(tài)
2.然后會把消息發(fā)送給所有的slave
3.slave在收到消息之后返回ack響應(yīng)給master
4.master在收到超過半數(shù)的ack之后感论,把消息標(biāo)記為committed
5.發(fā)送committed消息給所有slave绅项,slave也修改狀態(tài)為committed
RocketMQ為什么速度快】
是因?yàn)槭褂昧隧樞虼鎯Αage Cache和異步刷盤比肄、零拷貝http://www.reibang.com/writer#/notebooks/38316468/notes/103545244/preview。
我們在寫入commitlog的時候是順序?qū)懭氲哪叶福@樣比隨機(jī)寫入的性能就會提高很多
寫入commitlog的時候并不是直接寫入磁盤芳绩,而是先寫入操作系統(tǒng)的PageCache
最后由操作系統(tǒng)異步將緩存中的數(shù)據(jù)刷到磁盤