聊一聊幾款主流消息隊(duì)列之間的差異,我們應(yīng)該如何選擇
為什么需要消息隊(duì)列
消息隊(duì)列是歷史最悠久的中間件之一,它可以和不同的進(jìn)程進(jìn)行通信,從而實(shí)現(xiàn)上下游之間的消息傳遞?;诖颂匦裕覀兛梢栽谝韵氯齻€(gè)場景中使用消息隊(duì)列。
解耦;
限流;
流量削峰;
1)解耦
先來看解耦,假設(shè)有兩個(gè)服務(wù):A 和 B,當(dāng)服務(wù) A 依賴服務(wù) B 時(shí),請求的耗時(shí)就是這兩個(gè)服務(wù)之和。但如果服務(wù) B 耗時(shí)比較長怎么辦?
顯然這時(shí)服務(wù) A 可以將消息發(fā)送到隊(duì)列中,服務(wù) B 從隊(duì)列里面去取即可,從而實(shí)現(xiàn)兩個(gè)服務(wù)之間的邏輯解耦+物理解耦。
當(dāng)用戶注冊賬號時(shí),會(huì)將注冊信息發(fā)給賬號服務(wù),賬號服務(wù)將信息寫入數(shù)據(jù)庫后,會(huì)調(diào)用短信服務(wù)給用戶發(fā)送短信。如果不使用消息隊(duì)列,那么必須等短信發(fā)送成功之后才能返回。
但為了給用戶更好的體驗(yàn),我們可以將發(fā)送短信這一步獨(dú)立出去,賬號服務(wù)將用戶手機(jī)號和短信內(nèi)容投入消息隊(duì)列中就可以返回了,這樣用戶就能立刻收到注冊結(jié)果。而短信服務(wù)會(huì)消費(fèi)消息,異步執(zhí)行發(fā)送短信邏輯,這就是消息隊(duì)列的作用之一:解耦。
使用消息隊(duì)列進(jìn)行解耦,不僅可以提升性能,還可以使整個(gè)系統(tǒng)更加的模塊化。以電商為例,訂單服務(wù)是電商系統(tǒng)中的核心部分,它會(huì)被一系列下游服務(wù)依賴。并且隨著業(yè)務(wù)的發(fā)展,依賴訂單的下游服務(wù)會(huì)不斷增加、不斷變化。
因此負(fù)責(zé)訂單服務(wù)的開發(fā)團(tuán)隊(duì)不得不花費(fèi)大量精力,應(yīng)對不斷增加變化的下游服務(wù),不停地修改調(diào)試訂單服務(wù)與這些下游服務(wù)的接口。任何一個(gè)下游服務(wù)的接口產(chǎn)生變更,都需要訂單模塊重新進(jìn)行一次上線,對于一個(gè)電商的核心服務(wù)來說,這幾乎是不可接受的。
因此所有的電商系統(tǒng)都選擇用消息隊(duì)列,來解決這種系統(tǒng)耦合過于緊密的問題。引入消息隊(duì)列后,訂單服務(wù)在訂單變化時(shí)發(fā)送一條消息到消息隊(duì)列的一個(gè)主題 order 中,所有下游服務(wù)都訂閱主題 order,這樣每個(gè)下游服務(wù)都可以獲得一份實(shí)時(shí)完整的訂單數(shù)據(jù)。并且此時(shí)下游服務(wù)發(fā)生變化,不會(huì)影響訂單服務(wù)。
2)限流
一個(gè)完善的系統(tǒng)一定具備自我保護(hù)的能力,即使面對海量請求,也能盡最大努力去處理,處理不了的則會(huì)拒絕掉,從而保證系統(tǒng)運(yùn)行正常。因此如果我們能預(yù)估出系統(tǒng)的最大處理能力,就可以用消息隊(duì)列實(shí)現(xiàn)一個(gè)令牌桶,進(jìn)行流量控制。
令牌桶控制流量的原理是:單位時(shí)間內(nèi)發(fā)放固定數(shù)量的令牌到令牌桶中,規(guī)定服務(wù)在處理請求之前必須先從桶中取走一個(gè)令牌,如果桶里面沒有令牌,則拒絕請求。這樣就保證單位時(shí)間內(nèi),能處理的請求數(shù)不超過發(fā)放令牌的數(shù)量,起到了流量控制的作用。
令牌桶可以簡單地用一個(gè)有固定容量的消息隊(duì)列加一個(gè)令牌生成器來實(shí)現(xiàn):令牌生成器按照預(yù)估的處理能力,勻速生產(chǎn)令牌并放入令牌隊(duì)列(如果隊(duì)列滿了則丟棄令牌)。網(wǎng)關(guān)(流量的入口)在收到請求時(shí)從令牌隊(duì)列消費(fèi)一個(gè)令牌,獲取到令牌則繼續(xù)調(diào)用后端服務(wù),如果獲取不到令牌則直接返回失敗。
3)流量削峰
任何的大型服務(wù),特別是秒殺服務(wù),都離不開消息隊(duì)列。因?yàn)橄㈥?duì)列除了解耦和限流之外,還可以起到流量削峰的作用,就是緩沖瞬時(shí)的突發(fā)流量,使其更平滑。
對于那些發(fā)送能力很強(qiáng)的上游系統(tǒng),如果沒有消息隊(duì)列的保護(hù),脆弱的下游系統(tǒng)可能會(huì)直接被壓垮導(dǎo)致全鏈路服務(wù)雪崩。而一旦有了消息隊(duì)列,它就能夠有效地對抗上游的流量沖擊,避免了流量的震蕩。
我們舉一個(gè)實(shí)際的例子,比如在京東購買商品,當(dāng)點(diǎn)擊購買的時(shí)候,會(huì)調(diào)用訂單服務(wù)生成對應(yīng)的訂單。然而要處理該訂單則會(huì)依次調(diào)用下游的多個(gè)子服務(wù),比如查詢登錄信息、驗(yàn)證商品信息、確認(rèn)地址信息,調(diào)用銀行等支付接口進(jìn)行扣款等等。
顯然上游的訂單操作比較簡單,它的 TPS 要遠(yuǎn)高于處理訂單的下游服務(wù)。因此如果上游和下游直接對接,勢必會(huì)出現(xiàn)下游服務(wù)無法及時(shí)處理上游訂單從而造成訂單堆積的情況。特別是當(dāng)出現(xiàn)雙十一以及秒殺業(yè)務(wù)的時(shí)候,上游訂單流量會(huì)瞬間增加,可能出現(xiàn)的結(jié)果就是直接壓垮下游子系統(tǒng)服務(wù)。
解決此問題的一個(gè)做法是對上游的訂單服務(wù)進(jìn)行限流,比如采用上面說的令牌桶。但對于一個(gè)電商系統(tǒng)來說,這么做很明顯是不合適的,因?yàn)閱栴}不是出現(xiàn)在訂單服務(wù)上面,而且用戶買東西還限流,這樣錢送到嘴邊都賺不到。
所以會(huì)引入消息隊(duì)列來對抗這種上下游系統(tǒng)的 TPS 不一致以及瞬時(shí)的峰值流量,引入消息隊(duì)列之后,上游系統(tǒng)不再直接與下游系統(tǒng)進(jìn)行交互。當(dāng)新訂單生成之后它僅僅向隊(duì)列中發(fā)送一條消息,而下游消費(fèi)隊(duì)列中的消息,從而實(shí)現(xiàn)上游訂單服務(wù)和下游訂單處理服務(wù)的解耦。
這樣當(dāng)出現(xiàn)秒殺業(yè)務(wù)的時(shí)候,消息隊(duì)列能夠?qū)⑺矔r(shí)增加的訂單流量全部以消息的形式保存在隊(duì)列中,既不影響上游服務(wù)的 TPS,同時(shí)也給下游服務(wù)留出了足夠的時(shí)間去消費(fèi),這就是消息隊(duì)列存在的最大意義所在。
簡單來說,我們在單體應(yīng)用里面需要使用本地隊(duì)列解決的問題,在分布式系統(tǒng)中大多都可以用消息隊(duì)列來解決。但同時(shí)我們也要認(rèn)識到,消息隊(duì)列也有它的一些問題:
引入消息隊(duì)列帶來的延遲問題;
增加了系統(tǒng)的復(fù)雜度;
可能產(chǎn)生數(shù)據(jù)不一致的問題;
所以在軟件開發(fā)中沒有銀彈,需要根據(jù)業(yè)務(wù)的特點(diǎn)和自身?xiàng)l件選擇合適的架構(gòu)。
消息隊(duì)列該怎么選擇
消息隊(duì)列如同數(shù)據(jù)結(jié)構(gòu)一樣,沒有最好的,只有最合適的。但不管哪種消息隊(duì)列,如果想要用于生產(chǎn)環(huán)境中,都應(yīng)該具備以下幾個(gè)特點(diǎn):
消息的傳遞一定是可靠的;
支持阻塞等待拉取消息;
支持發(fā)布 / 訂閱模式;
具備 ack 機(jī)制,消費(fèi)失敗后可重新消費(fèi),消息不丟失;
實(shí)例宕機(jī)了,消息也不會(huì)丟失,也就是支持?jǐn)?shù)據(jù)持久化;
消息可積壓;
支持集群部署;
開源免費(fèi),社區(qū)具有一定的活躍度;
生態(tài)完善;
性能足夠好,能滿足絕大部分場景;
符合以上需求的消息隊(duì)列,主要有以下幾種。
1)RabbitMQ
RabbitMQ 是一個(gè)在 AMQP(高級消息隊(duì)列協(xié)議)基礎(chǔ)上完成的可復(fù)用的企業(yè)消息系統(tǒng),最早為電信行業(yè)系統(tǒng)之間的可靠通信而設(shè)計(jì),是當(dāng)前最主流的消息隊(duì)列之一。
早期的 RabbitMQ 只支持 AMQP 協(xié)議,現(xiàn)在也支持 MQTT 協(xié)議。
RabbitMQ 都具備哪些優(yōu)點(diǎn)呢?
采用 Erlang 語言編寫,Erlang 語言最初用于交換機(jī)領(lǐng)域,它有著和原生 socket 一樣的延遲,因此性能較好,吞吐量在萬級,并且時(shí)效性在微秒級。
功能完善,健壯、穩(wěn)定、易用、跨平臺。
支持大部分主流語言,文檔豐富,還提供了管理界面,并擁有非常高的社區(qū)活躍度和更新頻率。
有優(yōu)點(diǎn),自然就有缺點(diǎn),缺點(diǎn)如下:
RabbitMQ 對消息堆積的支持并不好,在它的設(shè)計(jì)理念里面,消息隊(duì)列是一個(gè)管道,大量的消息積壓是一種不正常的情況,應(yīng)當(dāng)盡量去避免。當(dāng)大量消息積壓的時(shí)候,會(huì)導(dǎo)致 RabbitMQ 的性能急劇下降。
RabbitMQ 的性能比較差,根據(jù)官方給出的測試數(shù)據(jù)以及使用經(jīng)驗(yàn),隨著硬件配置的不同,它大概每秒鐘可以處理幾萬到十幾萬條消息。其實(shí)這個(gè)性能也足夠支撐絕大多數(shù)的應(yīng)用場景了,但如果你的應(yīng)用對消息隊(duì)列的性能要求非常高,那就不適合選擇 RabbitMQ 了。
RabbitMQ 使用的編程語言 Erlang,這個(gè)編程語言不僅非常小眾,學(xué)習(xí)曲線也很陡峭。
2)RocketMQ
阿里巴巴開源的一款消息隊(duì)列,用 Java 語言實(shí)現(xiàn),在設(shè)計(jì)時(shí)參考了 Kafka,并做了一些改進(jìn)。在阿里內(nèi)部廣泛應(yīng)用于訂單、交易、重置、流計(jì)算、消息推送、日志流式處理、以及 binlog 分發(fā)等場景,經(jīng)歷過多次雙十一考驗(yàn),其性能、穩(wěn)定性和可靠性都是值得信賴的。
優(yōu)點(diǎn)如下:
支持單機(jī)吞吐量達(dá)到數(shù)十萬級,可用性高,分布式架構(gòu)保證消息零丟失;
功能較為完善,擴(kuò)展性好,支持 10 億級別的消息堆積,不會(huì)因?yàn)橄⒍逊e導(dǎo)致性能下降;
對在線業(yè)務(wù)的響應(yīng)時(shí)延做了很多的優(yōu)化,大多數(shù)情況下可以做到毫秒級的響應(yīng);
所以 RocketMQ 在吞吐量和消息堆積方面要比 RabbitMQ 高很多,如果你比較在意這兩個(gè)方面,那么可以使用 RocketMQ。而 RocketMQ 的缺點(diǎn)就是支持的客戶端語言不多,社區(qū)活躍度一般。
3)Kafka
大數(shù)據(jù)的殺手锏,談到大數(shù)據(jù)領(lǐng)域的消息傳輸,必然離不開 Kafka。這款為大數(shù)據(jù)而生的消息隊(duì)列,有著百分級 TPS 的吞吐量,在數(shù)據(jù)采集、傳輸、存儲(chǔ)的過程中發(fā)揮至關(guān)重要的作用,任何的大公司、或者做大數(shù)據(jù)的公司都離不開 Kafka。
Kafka 的特點(diǎn)是性能卓越,單機(jī)寫入 TPS 在百萬條每秒,時(shí)效性也在毫秒級。
Kafka 是分布式的,一個(gè)數(shù)據(jù)多個(gè)副本,少數(shù)的機(jī)器宕機(jī)也不會(huì)丟失數(shù)據(jù)。
消費(fèi)者采用 pull 方式獲取消息,消息有序、并且可以保證所有消息被消費(fèi)且僅被消費(fèi)一次。
擁有優(yōu)秀的第三方 Kafka Web 管理界面 Kafka-Manager,在日志領(lǐng)域比較成熟,在大數(shù)據(jù)領(lǐng)域的實(shí)時(shí)計(jì)算以及日志采集等場景中被大規(guī)模使用。
和周邊生態(tài)系統(tǒng)的兼容性非常好,在大數(shù)據(jù)和流計(jì)算領(lǐng)域,幾乎所有的開源軟件系統(tǒng)都會(huì)優(yōu)先支持 kafka。
Kafka 使用 Scala 和 Java 語言開發(fā),設(shè)計(jì)上大量使用了批量和異步的思想,這種設(shè)計(jì)使得 Kafka 能做到超高的性能。但也正是這種異步批量設(shè)計(jì)使得 Kafka 的響應(yīng)時(shí)延比較高,因?yàn)榭蛻舳税l(fā)送消息的時(shí)候,不會(huì)立即發(fā)出,而是攢夠一批之后一起發(fā)送。
所以 Kafka 不太適合在線業(yè)務(wù)場景,它的重點(diǎn)是吞吐量,而不是低延遲。并且 Kafka 還有如下缺點(diǎn):
單機(jī)超過 64 個(gè)分區(qū),CPU 使用率會(huì)發(fā)生明顯的飆高現(xiàn)象,隊(duì)列越多 CPU 使用率越高,發(fā)送消息響應(yīng)時(shí)間變長;
使用短輪詢方式,實(shí)時(shí)性取決于輪詢間隔時(shí)間,消費(fèi)失敗不支持重試;
雖然支持消息有序,但如果某臺機(jī)器宕機(jī),就會(huì)產(chǎn)生消息亂序。
那么這些消息隊(duì)列,我應(yīng)該選擇哪一種呢?
RabbitMQ:如果說消息隊(duì)列并不是你系統(tǒng)的主角之一,你對消息隊(duì)列的功能和性能都沒有很高的要求,只需要一個(gè)開箱即用易于維護(hù)的產(chǎn)品,建議使用 RabbitMQ。
RocketMQ:天生為金融領(lǐng)域而生,適合可靠性要求很高的場景,尤其是電商里面的訂單扣款、以及業(yè)務(wù)削峰。RocketMQ 在穩(wěn)定性上絕對值得信賴,畢竟這些業(yè)務(wù)場景在阿里雙十一已經(jīng)經(jīng)歷了多次考驗(yàn),如果你的業(yè)務(wù)也有類似場景,那么建議選擇 RocketMQ。
Kafka:基于 Pull 模式來處理消息,追求高吞吐量,一開始的目的就是用于日志收集和傳輸,高吞吐量是 Kakfka 的目標(biāo)。因此如果要處理海量的消息(比如日志采集、監(jiān)控信息、前端埋點(diǎn)),或者使用了大數(shù)據(jù)、流計(jì)算相關(guān)的開源產(chǎn)品,那么首選 Kafka。
消息隊(duì)列的存儲(chǔ)模型
任何一款消息隊(duì)列,都可以視為三部分:生產(chǎn)者、broker、消費(fèi)者。
生產(chǎn)者和消費(fèi)者都可以視為客戶端;
broker 便是服務(wù)端啟動(dòng)之后的進(jìn)程,比如 Kafka broker;
生產(chǎn)者會(huì)將消息發(fā)送至 broker 中,broker 會(huì)對消息進(jìn)行存儲(chǔ)以及持久化,消費(fèi)者負(fù)責(zé)從 broker 中拉取消息。如果拋開那些花里胡哨的概念,其實(shí)整個(gè)過程是非常簡單的。
而這里我們要探討的是,消息在隊(duì)列中是如何存儲(chǔ)的?
最初的消息隊(duì)列,就是一個(gè)嚴(yán)格意義上的隊(duì)列,它是一個(gè)先進(jìn)先出的線性表,通常使用鏈表或數(shù)組來實(shí)現(xiàn)。隊(duì)列只允許在后端(稱為 rear)進(jìn)行插入操作,在前端(稱為 front)進(jìn)行刪除操作。
這個(gè)定義里面包含幾個(gè)關(guān)鍵點(diǎn):
先進(jìn)先出:這意味著消息在入隊(duì)和出隊(duì)的過程中,需要保證這些消息嚴(yán)格有序,消息的寫入順序和讀取順序是一致的;
早期的消息隊(duì)列,就是按照隊(duì)列的數(shù)據(jù)結(jié)構(gòu)來設(shè)計(jì)的;
生產(chǎn)者發(fā)消息是入隊(duì)操作,消費(fèi)者收消息是出隊(duì)(刪除)操作;
服務(wù)端存放消息的容器自然就是隊(duì)列
如果有多個(gè)生產(chǎn)者往同一個(gè)隊(duì)列里面發(fā)消息,這個(gè)隊(duì)列中可以消費(fèi)到的消息就是這些生產(chǎn)者生產(chǎn)的所有消息的合集,消息的順序就是這些生產(chǎn)者發(fā)送消息的自然順序。
同理,如果有多個(gè)消費(fèi)者接收同一個(gè)隊(duì)列的消息,這些消費(fèi)者之間就是競爭關(guān)系,每個(gè)消費(fèi)者只能收到隊(duì)列中的一部分消息,也就是說任何一條消息只能被其中的一個(gè)消費(fèi)者收到。
如果需要將一份消息數(shù)據(jù)分發(fā)給多個(gè)消費(fèi)者,要求每個(gè)消費(fèi)者都能收到全量的消息,比如一份訂單數(shù)據(jù),要求風(fēng)控系統(tǒng)、分析系統(tǒng)、支付系統(tǒng)等都需要接收消息。這時(shí)單個(gè)隊(duì)列就滿足不了需求,一個(gè)可行的解決方式是,為每個(gè)消費(fèi)者創(chuàng)建一個(gè)單獨(dú)的隊(duì)列,讓生產(chǎn)者發(fā)送多份。
顯然這是個(gè)比較笨的做法,同樣的一份消息被復(fù)制到多個(gè)隊(duì)列中會(huì)浪費(fèi)資源。更重要的是,生產(chǎn)者必須知道有多少個(gè)消費(fèi)者,為每個(gè)消費(fèi)者單獨(dú)發(fā)送一份消息,這實(shí)際上違背了消息隊(duì)列的解耦這個(gè)設(shè)計(jì)初衷。
為了解決這個(gè)問題,演化出了另外一種消息模型:發(fā)布/訂閱模型。
在發(fā)布/訂閱模型中,消息的發(fā)送方被稱為發(fā)布者(Publisher),消息的接收方被稱為訂閱者(Subscriber),服務(wù)端存放消息的容器稱為主題(Topic)。發(fā)布者將消息發(fā)送到主題中,訂閱者在接收消息之前需要先訂閱主題,每份訂閱中,訂閱者都可以接收到主題的所有消息。
在消息領(lǐng)域的歷史上很長的一段時(shí)間,隊(duì)列模式和發(fā)布/訂閱模式是并存的,有些消息隊(duì)列同時(shí)支持這兩種消息模型。但我們仔細(xì)對比一下這兩種模型,會(huì)發(fā)現(xiàn)它們并沒有本質(zhì)的區(qū)別,生產(chǎn)者就是發(fā)布者,消費(fèi)者就是訂閱者,隊(duì)列就是主題。它們最大的區(qū)別其實(shí)就是,一份消息數(shù)據(jù)能不能被消費(fèi)多次的問題。
實(shí)際上,在這種發(fā)布/訂閱模型中,如果只有一個(gè)訂閱者,那它和隊(duì)列模型就基本是一樣的了,因此發(fā)布/訂閱模型在功能層面上可以兼容隊(duì)列模型。
現(xiàn)代的消息隊(duì)列產(chǎn)品使用的消息模型大多是這種發(fā)布/訂閱模型,當(dāng)然也有例外。
RabbitMQ 的消息模型
RabbitMQ 是少數(shù)依然堅(jiān)持使用隊(duì)列模型的產(chǎn)品之一,那它是怎么解決多個(gè)消費(fèi)者的問題呢?在 RabbitMQ 里面有一個(gè)別的消息隊(duì)列都沒有的概念:Exchange(交換機(jī)),它位于生產(chǎn)者和隊(duì)列之間,生產(chǎn)者并不關(guān)心將消息發(fā)送給哪個(gè)隊(duì)列,而是將消息發(fā)送給 Exchange,由 Exchange 上配置的策略來決定將消息投遞到哪些隊(duì)列中。
同一份消息如果需要被多個(gè)消費(fèi)者消費(fèi),則需要配置 Exchange 將消息發(fā)送到多個(gè)隊(duì)列,每個(gè)隊(duì)列中都存放一份完整的消息數(shù)據(jù),可以為一個(gè)消費(fèi)者提供消費(fèi)服務(wù)。這也可以變相地實(shí)現(xiàn)發(fā)布/訂閱模型中,一份消息數(shù)據(jù)可以被多個(gè)訂閱者來多次消費(fèi)這樣的功能。
所以RabbitMQ 消息傳遞模型的核心思想是:生產(chǎn)不會(huì)直接將消息發(fā)送到隊(duì)列,而是先發(fā)送到交換機(jī)。交換機(jī)的工作內(nèi)容就是接收生產(chǎn)者的消息,并且按照指定的規(guī)則將消息推入隊(duì)列,因此交換機(jī)必須清楚地知道如何處理接收到的消息,是把這些消息推送到特定隊(duì)列、還是多個(gè)隊(duì)列,亦或是丟棄它們,這要由交換機(jī)的類型決定。
交換機(jī)有 4 種類型:direct、fanout、topic、headers,默認(rèn)是 direct,不同的類型的交換機(jī)會(huì)有不同的表現(xiàn)。
RabbitMQ 會(huì)通過 Binding 將 Exchange 和 Queue 綁定在一起,并且在綁定 Exchange 和 Queue 的同時(shí)(可多次綁定),會(huì)指定一個(gè) Binding key。而生產(chǎn)者將消息發(fā)送到 Exchange 的時(shí)候,也會(huì)帶上一個(gè) Routing key。
如果交換機(jī)的類型是 direct,它會(huì)將消息推送到 Binding Key 和 Routing Key 相匹配的 Queue 中。因?yàn)榻粨Q機(jī)和隊(duì)列可以多次綁定,所以一個(gè)隊(duì)列可以有多個(gè) Binding Key,只要一個(gè)能匹配上即可;
如果交換機(jī)的類型是 fanout,它會(huì)直接把消息推送到所有與它綁定的隊(duì)列中;
如果交換機(jī)的類型是 topic,那么 Binding Key 會(huì)支持 * 通配符,從而和 Routing Key 進(jìn)行模糊匹配;
如果交換機(jī)類型是 headers,會(huì)根據(jù)發(fā)送的消息內(nèi)容中的 headers 屬性進(jìn)行匹配;
RocketMQ 的消息模型
RocketMQ 使用標(biāo)準(zhǔn)的發(fā)布/訂閱模型,但它除了生產(chǎn)者、消費(fèi)者和主題之外,也有隊(duì)列這個(gè)概念,并且隊(duì)列在 RocketMQ 中是一個(gè)非常重要的概念。每個(gè)主題包含多個(gè)隊(duì)列,通過多個(gè)隊(duì)列來實(shí)現(xiàn)并行生產(chǎn)和消費(fèi)。需要注意的是,RocketMQ 只在隊(duì)列上保證消息的有序性,主題層面是無法保證消息的嚴(yán)格順序的。
RocketMQ 中,訂閱者的概念是通過消費(fèi)者組(Consumer Group)來體現(xiàn)的,每個(gè)消費(fèi)者組都消費(fèi)主題中一份完整的消息,不同消費(fèi)者組之間消費(fèi)進(jìn)度彼此不受影響。也就是說,一條消息被 Consumer Group1 消費(fèi)過,也可以再給 Consumer Group2 消費(fèi)。
但消費(fèi)者組中包含多個(gè)消費(fèi)者,同一個(gè)組內(nèi)的消費(fèi)者是競爭關(guān)系,每個(gè)消費(fèi)者負(fù)責(zé)消費(fèi)組內(nèi)的一部分消息。如果一條消息被消費(fèi)者 Consumer1 消費(fèi)了,那同組的其它消費(fèi)者就不會(huì)再收到這條消息。
在 Topic 的消費(fèi)過程中,由于消息需要被不同的組進(jìn)行多次消費(fèi),所以消費(fèi)完的消息并不會(huì)立即被刪除,這就需要 RocketMQ 為每個(gè)消費(fèi)者組在每個(gè)隊(duì)列上維護(hù)一個(gè)消費(fèi)位移(Consumer Offset)。這個(gè)位移之前的消息都被消費(fèi)過,之后的消息都沒有被消費(fèi)過,每成功消費(fèi)一條消息,消費(fèi)位移就加一。
如果你對 RocketMQ 中的這些概念還有些困惑的話,那么沒關(guān)系,我們看一下 Kafka 的消息模型。如果你熟悉 Kafka 的話,那么你會(huì)瞬間理解 RocketMQ。
Kafka 的消息模型
Kafka 的消息模型和 RocketMQ 是完全一樣的,上面說的所有 RocketMQ 中的概念,和生產(chǎn)消費(fèi)過程中的確認(rèn)機(jī)制,都完全適用于 Kafka。唯一的區(qū)別是,在 Kafka 中,隊(duì)列這個(gè)概念的名稱不一樣,Kafka 中對應(yīng)的名稱是分區(qū)(Partition),但含義以及功能和 RocketMQ 的隊(duì)列是沒有任何區(qū)別的。
所以如果你熟悉 Kafka,那么你會(huì)瞬間理解 RocketMQ,因?yàn)閮烧叩南⒛P褪且粯拥?。只不過 RocketMQ 是一個(gè)主題對應(yīng)多個(gè)隊(duì)列,而 Kafka 是一個(gè)主題對應(yīng)多個(gè)分區(qū)。
小結(jié)
以上我們就探討了消息隊(duì)列的應(yīng)用場景,以及它們存儲(chǔ)模型之間差異。關(guān)于這些隊(duì)列更詳細(xì)的內(nèi)容,可以參考網(wǎng)上的資料。
總之當(dāng)你的數(shù)據(jù)量不大時(shí),使用 RabbitMQ 是一個(gè)不錯(cuò)的選擇。
- 上一篇
數(shù)字化轉(zhuǎn)型的成功之道
近年來,全球數(shù)字化轉(zhuǎn)型的浪潮愈演愈烈,企業(yè)紛紛投入大量資源進(jìn)行技術(shù)更新與升級。然而,令人擔(dān)憂的是,盡管支出規(guī)模龐大,但成功的案例卻寥寥可數(shù)。
- 下一篇
云存儲(chǔ)領(lǐng)域的三大誤區(qū)
混合方法是確保優(yōu)化存儲(chǔ)解決方案選擇的最佳方式。通過混合使用公有、私有或內(nèi)部云資源來存儲(chǔ)和管理數(shù)據(jù),公司可以避免被鎖定,并可以選擇適合其特定數(shù)據(jù)訪問需求的服務(wù)——具體取決于數(shù)據(jù)的使用方式和用戶。