消息中间件:RocketMQ 介绍(特性、术语、原理、优缺点、消息顺序、消息重复)
前些天發(fā)現(xiàn)了一個(gè)巨牛的人工智能學(xué)習(xí)網(wǎng)站,通俗易懂,風(fēng)趣幽默,忍不住分享一下給大家。點(diǎn)擊跳轉(zhuǎn)到教程。
消息中間件的作用
1. 應(yīng)用解耦
2. 異步處理
比如用戶注冊(cè)場(chǎng)景,注冊(cè)主流程完成以后,需要調(diào)用郵件系統(tǒng)發(fā)送郵件通知用戶注冊(cè)成功,可能還需要調(diào)用其他系統(tǒng)。這是串行的,如果一個(gè)系統(tǒng)依賴很多系統(tǒng),那么這個(gè)主流程會(huì)比較長(zhǎng),耦合度高,整個(gè)系統(tǒng)維護(hù)成本也會(huì)越來(lái)越高。那么我們就可以使用消息中間件來(lái)進(jìn)行解耦,通過(guò)發(fā)布訂閱模式,完成用戶注冊(cè)之后,向中間件發(fā)送消息,這樣就可以馬上給用戶返回,至于后續(xù)工作其他系統(tǒng)向中間件訂閱這個(gè)消息并完成后續(xù)工作就好。這也就是一個(gè)解耦和異步處理過(guò)程。
?
中間件有下面兩種模型
點(diǎn)對(duì)點(diǎn)模型
發(fā)布訂閱模型
?
消息中間件的解耦和異步是兩個(gè)最重要的需求點(diǎn),除此之外還應(yīng)該做一些其他事情比如:
-
保證一致性,產(chǎn)生消息和發(fā)送消息是一致的,也就是如果操作成功,那么消息一定發(fā)送成功;如果業(yè)務(wù)操作沒(méi)有成功那么就不能發(fā)送消息
-
具備一定消息堆積能力,可以為后端擋住一些數(shù)據(jù)流保證后端不會(huì)被壓垮
-
具備消息實(shí)時(shí)性,保證消息的低延遲
-
具備消息的可靠性,主要是可靠地存儲(chǔ)和投遞
?
消息系統(tǒng)里面應(yīng)該有這樣一個(gè)假設(shè):消息一定會(huì)堆積。下游系統(tǒng)通常有很多,里面有重要的也不重要的,面對(duì)突發(fā)流量高峰,一定會(huì)有后端系統(tǒng)處理不過(guò)來(lái)的情況,從而造成消息堆積;當(dāng)然還有一種情況是后端系統(tǒng)出現(xiàn)問(wèn)題導(dǎo)致暫時(shí)無(wú)法消費(fèi)消息從而造成消息中間件的消息堆積。所以中間件要起到蓄水池的作用。
?
數(shù)據(jù)一致性,這個(gè)很容易理解,因?yàn)槭欠植际疆惒降?#xff0c;但是又不能容忍數(shù)據(jù)出錯(cuò),所以在性能和數(shù)據(jù)一致性方面就需要有所妥協(xié),通常在互聯(lián)網(wǎng)行業(yè)中采取最終一致性。需要注意的是最終一致性和弱一致性不同,弱一致性表示允許在異常情況下數(shù)據(jù)可能不一致,而最終一致性則是在某段時(shí)間內(nèi)允許不一致但是最終會(huì)一致。
?
RocketMQ介紹
基于發(fā)布訂閱的隊(duì)列模型消息中間件,它只有發(fā)布和訂閱的消息方式,消息類型只支持Message,消息可以持久化。服務(wù)端使用JAVA編寫,客戶端支持JAVA、C++。阿里2012年開源,之后作為Apache基金會(huì)的一個(gè)項(xiàng)目進(jìn)行維護(hù)。是一款低延遲、高可靠、可伸縮、易于使用的中間件。在Github上有相關(guān)介紹。
?
特性
消息可靠性:
-
生產(chǎn)者的可靠性保證:生產(chǎn)者發(fā)送消息后返回SendResult,如果isSuccess返回true,則表示消息已經(jīng)確認(rèn)發(fā)送到服務(wù)器并被服務(wù)器接收保存。整個(gè)發(fā)送過(guò)程是一個(gè)同步過(guò)程。
-
服務(wù)器的可靠性:消息生產(chǎn)者發(fā)送的消息,RocketMQ服務(wù)收到后在做必要的校驗(yàn)和檢查之后馬上保存到磁盤,寫入成功后返回給生產(chǎn)者。因此可以確認(rèn)每條發(fā)送結(jié)果為成功的消息都會(huì)被消息服務(wù)器寫入磁盤。
-
消費(fèi)者的可靠性:消費(fèi)者是一條一條順序消費(fèi)的,之后在成功消費(fèi)一條后才會(huì)消費(fèi)嚇一跳。如果在消費(fèi)某一條消息時(shí)失敗則會(huì)重試消費(fèi)這條消息,默認(rèn)為5次,如果超過(guò)最大次數(shù)仍然無(wú)法消費(fèi),則將消息保存到本地,后臺(tái)線程繼續(xù)重試消費(fèi),主線程則會(huì)繼續(xù)往后走,消費(fèi)隊(duì)列后面的消息。
?
消息持久性:RocketMQ收到消息后,會(huì)將消息持久化到文件,并利用Linux文件系統(tǒng)內(nèi)存來(lái)提高性能
消息實(shí)時(shí)性:RocketMQ采取長(zhǎng)輪詢+PULL模式保證消息的持久性
消息重復(fù):對(duì)于消費(fèi)者來(lái)說(shuō),通過(guò)拉取方式將消息保存到本地,消費(fèi)完再向服務(wù)器返回,在網(wǎng)絡(luò)異常的情況下可能會(huì)出現(xiàn)重復(fù)。
消息過(guò)濾:
-
服務(wù)器端過(guò)濾:減少不必要消息傳輸,但是會(huì)增加服務(wù)器負(fù)擔(dān)
-
客戶端過(guò)濾:根據(jù)客戶端需求來(lái)定制消息,缺點(diǎn)是客戶端會(huì)收到對(duì)它來(lái)說(shuō)沒(méi)用的消息,如果客戶端無(wú)法承載這么多消息就會(huì)導(dǎo)致故障
消息堆積:支持10億級(jí)別的消息堆積,不會(huì)因?yàn)橄⒍逊e影響性能
?
術(shù)語(yǔ)說(shuō)明
?
| 角色 | 說(shuō)明 |
| Producer | Producer: 生產(chǎn)者,用于將消息發(fā)送到RocketMQ,生產(chǎn)者本身既可以是生成消息,也可以對(duì)外提供接口,由外部來(lái)調(diào)用接口,再由生產(chǎn)者將受到的消息發(fā)送給MQ。 |
| Consumer | Consumer: 消費(fèi)者,從Broker拉取消息進(jìn)行消費(fèi)。從應(yīng)用角度來(lái)說(shuō)有兩類消費(fèi)者:
|
| Broker | Broker: RocketMQ服務(wù)器,也是整個(gè)服務(wù)的核心,它實(shí)現(xiàn)了消息的存儲(chǔ)、拉取功能。它通常以集群方式啟動(dòng),并可配置主從,每個(gè)broker上提供對(duì)指定topic的服務(wù)。理解了broker的原理以及它和其他服務(wù)交互的過(guò)程,也就命令消息中間件的原理,其實(shí)都大同小異。它具有2中角色
|
| Topic | Topic: 消息的主題,由用于定義并在服務(wù)端配置,消費(fèi)者可以按照主題進(jìn)行訂閱,也就是消息分類,通常一個(gè)應(yīng)用一個(gè)Topic |
| Message | Message: 在生產(chǎn)者、消費(fèi)者、服務(wù)器之間傳遞的消息,一個(gè)message必須屬于一個(gè)Topic |
| Namesrv | Namesrv: 一個(gè)無(wú)狀態(tài)的名稱服務(wù),可以集群部署,每一個(gè)broker啟動(dòng)的時(shí)候都會(huì)向名稱服務(wù)器注冊(cè),主要是接收broker的注冊(cè)(broker每十秒就會(huì)向所有名稱服務(wù)器發(fā)送心跳請(qǐng)求,同時(shí)注冊(cè)topic信息到名稱服務(wù)器),接收客戶端的路由請(qǐng)求并返回路由信息,你可以理解為服務(wù)自動(dòng)發(fā)現(xiàn),就是相當(dāng)于zookeeper在dubbo框架中的作用。
|
| Group | Group: 組名,一類消費(fèi)者或者生產(chǎn)者的集合名稱。
|
| Offset | Offset: 偏移量,消費(fèi)者拉取消息時(shí)需要知道上一次消費(fèi)到了什么位置,這一次從哪里開始。 |
| Partition | Partition: 分區(qū),Topic物理上的分組,一個(gè)Topic可以分為多個(gè)分區(qū),每個(gè)分區(qū)是一個(gè)有序的隊(duì)列。分區(qū)中的每條消息都會(huì)給分配一個(gè)有序的ID,也就是偏移量。 分區(qū)的目的:
Topic是消息的邏輯隊(duì)列,分區(qū)是物理隊(duì)列??梢酝ㄟ^(guò)配置文件來(lái)設(shè)置topic的默認(rèn)分區(qū)數(shù)量,也可以在新建立topic的時(shí)候指定。建議分區(qū)數(shù)量和消費(fèi)者數(shù)量一致,因?yàn)橄M(fèi)者數(shù)量多,多出來(lái)的不會(huì)去消費(fèi)消息的,因?yàn)橐粋€(gè)隊(duì)列只能被一個(gè)消費(fèi)者消費(fèi)。如果消費(fèi)者數(shù)量少則消費(fèi)者就會(huì)比較繁忙。 |
| Tag | Tag: 用于對(duì)消息進(jìn)行過(guò)濾,理解文件message的子主題,同一業(yè)務(wù)不同目的的message可以用相同的topic但是可以用不同的tag來(lái)區(qū)分,在隊(duì)列中tag在消息的數(shù)據(jù)結(jié)構(gòu)中被 轉(zhuǎn)換為一個(gè)8byte的hashcode,這樣節(jié)省空間。過(guò)濾分兩步: 在Broker端進(jìn)行Message Tag對(duì)比,先遍歷Consume Queue,如果存儲(chǔ)的Message tag與訂閱的tag不符合就跳過(guò),符合則傳輸給Consumer,在隊(duì)列中繼續(xù)比對(duì)hashcode Consumer收到消息后,對(duì)比真實(shí)的Message Tag字符串,而不是Hashcode,這樣避免HASH沖突。 |
| key | key: 消息的KEY字段是為了唯一表示消息的,方便查問(wèn)題,不是說(shuō)必須設(shè)置,只是說(shuō)設(shè)置為了方便開發(fā)和運(yùn)維定位問(wèn)題,這個(gè)KEY可以是訂單ID等。 |
?
原理
消費(fèi)者:
-
Push Consumer,應(yīng)用向Consumer對(duì)象注冊(cè)一個(gè)Listener接口,一但收到消息,Consumer對(duì)象立刻回調(diào)Listener接口方法
-
Pull Consumer,應(yīng)用主動(dòng)調(diào)用Consumer的拉取消息方法,從Broker拉消息
消費(fèi)模式:
-
廣播模式:一條消息被多個(gè)消費(fèi)者消費(fèi),即使它們屬于同一個(gè)消費(fèi)者組,消息會(huì)被組中的每個(gè)成員消費(fèi)一次。
-
集群模式:消息會(huì)被平均分配到消費(fèi)者組中進(jìn)行消費(fèi)。
消息模式:
-
順序消息:消息的消費(fèi)順序要和發(fā)送的順序一致,一類消息為滿足順序性,生產(chǎn)者必須單線程順序發(fā)送且發(fā)送到同一個(gè)隊(duì)列,這樣消費(fèi)者就可以按照生產(chǎn)者發(fā)送的順序去消費(fèi)。
-
普通順序消息:正常情況下可以保證完全順序消費(fèi),但是一旦發(fā)生異常,比如broker重啟,由于隊(duì)列總數(shù)發(fā)生變化,會(huì)產(chǎn)生短暫的消息順序不一致。如果業(yè)務(wù)可以容忍這種異常情況則可以使用。
-
嚴(yán)格順序消息:無(wú)論任何情況下都必須保證消息的順序,但是這就犧牲分布式的高可用功能,也就是Broker集群中只要有一臺(tái)不可用,那么整個(gè)集群就不可用。如果集群部署模式為同步雙寫模式,那么可以通過(guò)備機(jī)自動(dòng)切換來(lái)避免,不過(guò)仍然存在短暫間隙的服務(wù)不可用。
消息的存儲(chǔ)
生產(chǎn)者上產(chǎn)消息,根據(jù)Topic選擇其對(duì)應(yīng)的某一個(gè)分區(qū),然后發(fā)送到這個(gè)分區(qū)所在的Brocker上,消費(fèi)者根據(jù)訂閱的Topic選擇去Topic的某一個(gè)分區(qū)拉取消息。
RocketMQ收到消息后會(huì)把消息保存在本地文件中,每個(gè)文件最大上線1G,如果寫入消息時(shí)超過(guò)當(dāng)前文件大小,會(huì)建立一個(gè)新文件,文件名為起始字節(jié)大小。消息寫入是順序的,讀取是隨機(jī)的,因?yàn)閿?shù)據(jù)持久化當(dāng)前寫入文件只有一個(gè),所以可以是順序?qū)懭?#xff0c;但是讀取的時(shí)候因?yàn)橛卸鄠€(gè)邏輯隊(duì)列,每個(gè)邏輯隊(duì)列由多個(gè)分區(qū)所以就出現(xiàn)多個(gè)邏輯讀隊(duì)列,這樣讀取的時(shí)候就是隨機(jī)的。如何提高讀取性能呢?就是盡可能讓讀命中系統(tǒng)pageCache,減少磁盤IO次數(shù)。RcoketMQ的持久化是先寫入pageCache頁(yè)面高速緩存,然后刷盤,這樣保證內(nèi)存與磁盤都有一份相同的數(shù)據(jù),訪問(wèn)時(shí)直接從內(nèi)存讀取。另外一方面RocketMQ在文件讀寫方面做了優(yōu)化,采用內(nèi)存映射方式完成,也就是把磁盤文件映射到內(nèi)存地址空間,避免了內(nèi)核空間到用戶空間的復(fù)制。
支持的部署架構(gòu)
?
| 集群方式 | 消息可靠性(Master宕機(jī)) | 服務(wù)可用性 | 特點(diǎn) | 其他說(shuō)明 |
| 一組主主 | 同步刷盤消息一條都不會(huì)丟失 | 整體可用,未被消費(fèi)的消息無(wú)法取得,影響實(shí)時(shí)性 | 結(jié)構(gòu)簡(jiǎn)單、擴(kuò)容方便、性能最高 | 適合消息可靠性高,實(shí)時(shí)性低的需求 |
| 一組主從 | 異步有毫秒級(jí)丟失,同步雙寫不丟失 | 主備不能切換,且備機(jī)只能讀不能寫,會(huì)造成服務(wù)整體不可用 | ? | 不推薦使用 |
| 多組主從 (異步復(fù)制) | 故障是會(huì)丟失消息 | 整體可用,實(shí)時(shí)性影響是毫秒級(jí)別,該組服務(wù)只能讀不能寫 | 結(jié)構(gòu)復(fù)雜、擴(kuò)容方便,性能很高。 | 適合消息可靠性中等,實(shí)時(shí)性要求中等的場(chǎng)景 |
| 多組主從(同步雙寫) | 不丟消息 | 整體可用,不影響實(shí)時(shí)性。該組服務(wù)只能讀不能寫。不能自動(dòng)切換。 | 結(jié)構(gòu)復(fù)雜,擴(kuò)容方便,性能比異步低一點(diǎn),所以實(shí)時(shí)性也并不比異步方式高太多。 | 適合消息可靠性高,實(shí)時(shí)性中等,性能要求不高的場(chǎng)景。 |
推薦的架構(gòu)如下:
高要求則使用多組主從同步雙寫,低要求使用主主方案。
應(yīng)用場(chǎng)景
-
RocketMQ應(yīng)用到Cache,可以用在大量機(jī)器同步信息的場(chǎng)景
-
業(yè)務(wù)削峰,在大量交易涌入時(shí),后端可能無(wú)法及時(shí)處理,所以MQ的大量消息堆積功能就可以發(fā)揮作用。
-
日志收集,RocketMQ的設(shè)計(jì)模型從Kafka衍生而來(lái),kafka在日志收集系統(tǒng)中充當(dāng)緩沖功能,隨意RocketMQ也適用此場(chǎng)景
-
對(duì)可靠性要求很高的場(chǎng)景,尤其是電商里面的訂單扣款,因?yàn)榭劭钜婕暗胶芏嗟谌街Ц丁?/p>
優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
-
順序性,它支持順序性,可以做到局部有序,在單線程內(nèi)使用該生產(chǎn)者發(fā)送的消息按照發(fā)送的順序到達(dá)服務(wù)器并存儲(chǔ),并按照相同順序被消費(fèi),但前提是這些消息發(fā)往同一服務(wù)器的同一個(gè)分區(qū)
-
實(shí)時(shí)性:采取長(zhǎng)輪詢+PULL消費(fèi)消息,你可以自己決定如何在響應(yīng)性和吞吐量之間做平衡,配合合理的參數(shù)設(shè)置來(lái)獲得更高的響應(yīng)時(shí)間,實(shí)時(shí)性不低于PUSH方式
-
提供了豐富的拉取模式
-
支持10億級(jí)別的消息堆積,不會(huì)因?yàn)槎逊e導(dǎo)致性能下降
-
高效的訂閱者水平擴(kuò)展機(jī)制
缺點(diǎn)
-
消息重復(fù)問(wèn)題,它不能保證不重復(fù),只能保證正常情況下不重復(fù)
-
不支持分布式事務(wù)
-
消息過(guò)濾功能擴(kuò)展比較單一
?
消息順序
消息順序是只可以按照消息發(fā)送的順序進(jìn)行消費(fèi)。一個(gè)訂單產(chǎn)生3條消息,訂單創(chuàng)建、付款、訂單完成。消費(fèi)時(shí)只有按照順序消費(fèi)才有意義,不可能先消費(fèi)付款消息再消費(fèi)訂單創(chuàng)建消息,這樣就亂了。另外,多筆訂單又可以并行消費(fèi)。如何保證呢?
一個(gè)訂單產(chǎn)生的消息只能發(fā)送給同一個(gè)MQ服務(wù)器中的同一個(gè)分區(qū),并且按順序發(fā)送,這樣才能在理論上保證消費(fèi)者消費(fèi)時(shí)是按照順序消費(fèi)的,因?yàn)橐粋€(gè)分區(qū)就是一個(gè)邏輯隊(duì)列。生產(chǎn)者雖然按順序發(fā)送,但是第一條消息到達(dá)MQ的耗時(shí)比第二條多,那么第二條則會(huì)被先消費(fèi),這樣就又導(dǎo)致消費(fèi)時(shí)不是順序的。那么如何解決呢?可以采取只有第一條被消費(fèi)者消費(fèi)成功后再發(fā)送第二條。看下圖:
但是如果第一條被發(fā)送到消費(fèi)者后,消費(fèi)者沒(méi)有響應(yīng)(消費(fèi)者發(fā)送響應(yīng)但是因?yàn)榫W(wǎng)絡(luò)問(wèn)題丟失或者消費(fèi)者就沒(méi)有收到消息),那么在這種情況下你是繼續(xù)發(fā)送第二條還是重發(fā)第一條呢?如果是嚴(yán)格消息順序,那肯定是重發(fā)第一條,但是如果是消費(fèi)者消費(fèi)后的響應(yīng)丟失了,那么重發(fā)第一條就會(huì)造成重復(fù)消費(fèi)。
?
從另外一方面看,如果不考慮網(wǎng)絡(luò)異常,那么要實(shí)現(xiàn)嚴(yán)格消息,就必須采取一種一對(duì)一關(guān)系,生產(chǎn)者A的消息對(duì)應(yīng)到MQ服務(wù)器1的X隊(duì)列,消費(fèi)者A消費(fèi)X隊(duì)列。這樣串行結(jié)構(gòu)就會(huì)造成系統(tǒng)吞吐量太低;更多異常需要處理比如消費(fèi)端出現(xiàn)問(wèn)題,那么整個(gè)消息隊(duì)列就會(huì)出現(xiàn)阻塞。RocketMQ通過(guò)輪詢所有隊(duì)列來(lái)確定消息發(fā)送到哪一個(gè)隊(duì)列(負(fù)載均衡),比如相同訂單號(hào)的消息會(huì)被先后發(fā)送到統(tǒng)一隊(duì)列中。所以RocketMQ
?
消息重復(fù)
造成消費(fèi)重復(fù)的根本原因是網(wǎng)絡(luò)不可達(dá),只要有網(wǎng)絡(luò),這種網(wǎng)絡(luò)的不穩(wěn)定因素就存在你無(wú)法規(guī)避。所以解決這個(gè)問(wèn)題的最好辦法就是繞過(guò)它。這就變成了,消費(fèi)端收到兩個(gè)一樣的消息后如何處理,而不是從發(fā)送端解決不發(fā)送2個(gè)一樣的消息。對(duì)于消費(fèi)端的要求就是:
-
消費(fèi)端處理業(yè)務(wù)消息要保持冪等性,也就是同一個(gè)東西執(zhí)行多次會(huì)得到相同結(jié)果
-
保證每條消息都有唯一編號(hào)切保證消息處理成功與去重表的日志同時(shí)出現(xiàn)
第一條好理解,第二條就是利用一張日志表來(lái)記錄已經(jīng)處理成功的消息ID,如果新到的消息ID已經(jīng)存在表中那么就不再處理這個(gè)消息。第一條是在消費(fèi)端實(shí)現(xiàn)的,不屬于消息系統(tǒng)的功能;第二條可以是消息系統(tǒng)實(shí)現(xiàn)也可以是業(yè)務(wù)端實(shí)現(xiàn),處于對(duì)消息系統(tǒng)的吞吐量和高可用考慮最好還是由消費(fèi)端去處理。所以這也就是RocketMQ不解決消息重復(fù)的原因。
轉(zhuǎn)自:http://blog.51cto.com/littledevil/2068474
? ? ? ? ? ?http://blog.51cto.com/littledevil/2068548
? ? ? ? ? ?http://blog.51cto.com/littledevil/2068718
總結(jié)
以上是生活随笔為你收集整理的消息中间件:RocketMQ 介绍(特性、术语、原理、优缺点、消息顺序、消息重复)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 解决 idea 中 jsp 修改后页面
- 下一篇: Qt自定义事件实现及子线程向主线程传送事