IM系統(tǒng)的MQ消息中間件選型:Kafka還是RabbitMQ?
Posted on 2018-06-12 15:13 Jack Jiang 閱讀(1040) 評(píng)論(0) 編輯 收藏1、前言
在IM這種講究高并發(fā)、高消息吞吐的互聯(lián)網(wǎng)場(chǎng)景下,MQ消息中間件是個(gè)很重要的基礎(chǔ)設(shè)施,它在IM系統(tǒng)的服務(wù)端架構(gòu)中擔(dān)當(dāng)消息中轉(zhuǎn)、消息削峰、消息交換異步化等等角色,當(dāng)然MQ消息中間件的作用遠(yuǎn)不止于此,它的價(jià)值不僅僅存在于技術(shù)上,更重要的是改變了以往同步處理消息的思路(比如進(jìn)行IM消息歷史存儲(chǔ)時(shí),傳統(tǒng)的信息系統(tǒng)作法可能是收到一條消息就馬上同步存入數(shù)據(jù)庫,這種作法在小并發(fā)量的情況下可以很好的工作,但互聯(lián)網(wǎng)大并發(fā)環(huán)境下就是災(zāi)難)。
MQ消息中間件可以理解一個(gè)水池,水池的這頭是消息生產(chǎn)者,水池的那頭是消息消費(fèi)者,生產(chǎn)者和消息者無需直接對(duì)接,這將帶來很多好處:業(yè)務(wù)解耦、架構(gòu)分布式化等,生產(chǎn)者和消費(fèi)者互相完全透明。
但市面上的MQ消息中間件產(chǎn)品很多,作為IM系統(tǒng)中必不可少的一環(huán),我們?cè)撊绾芜x型?那么請(qǐng)繼續(xù)閱讀本文。
學(xué)習(xí)交流:
- 即時(shí)通訊開發(fā)交流3群:185926912[推薦]
- 移動(dòng)端IM開發(fā)入門文章:《新手入門一篇就夠:從零開發(fā)移動(dòng)端IM》
(本文同步發(fā)布于:http://www.52im.net/thread-1647-1-1.html)
2、關(guān)于作者
朱忠華:主要從事消息中間件相關(guān)的研發(fā)工作,著有《RabbitMQ實(shí)戰(zhàn)指南》一書,作者的博客是:https://blog.csdn.net/u013256816/article/details/78785569。
3、內(nèi)容概述
消息隊(duì)列中間件(簡(jiǎn)稱消息中間件)是指利用高效可靠的消息傳遞機(jī)制進(jìn)行與平臺(tái)無關(guān)的數(shù)據(jù)交流,并基于數(shù)據(jù)通信來進(jìn)行分布式系統(tǒng)的集成。通過提供消息傳遞和消息排隊(duì)模型,它可以在分布式環(huán)境下提供應(yīng)用解耦、彈性伸縮、冗余存儲(chǔ)、流量削峰、異步通信、數(shù)據(jù)同步等等功能,其作為分布式系統(tǒng)架構(gòu)中的一個(gè)重要組件,有著舉足輕重的地位。
目前開源的消息中間件可謂是琳瑯滿目,能讓大家耳熟能詳?shù)木陀泻芏啵热?ActiveMQ、RabbitMQ、Kafka、RocketMQ、ZeroMQ 等。不管選擇其中的哪一款,都會(huì)有用的不趁手的地方,畢竟不是為你量身定制的。有些大廠在長(zhǎng)期的使用過程中積累了一定的經(jīng)驗(yàn),其消息隊(duì)列的使用場(chǎng)景也相對(duì)穩(wěn)定固化,或者目前市面上的消息中間件無法滿足自身需求,并且也具備足夠的精力和人力而選擇自研來為自己量身打造一款消息中間件。但是絕大多數(shù)公司還是不會(huì)選擇重復(fù)造輪子,那么選擇一款合適自己的消息中間件顯得尤為重要。就算是前者,那么在自研出穩(wěn)定且可靠的相關(guān)產(chǎn)品之前還是會(huì)經(jīng)歷這樣一個(gè)選型過程。
在整體架構(gòu)中引入消息中間件,勢(shì)必要考慮很多因素,比如成本及收益問題,怎么樣才能達(dá)到最優(yōu)的性價(jià)比?雖然消息中間件種類繁多,但是各自都有各自的側(cè)重點(diǎn),選擇合適自己、揚(yáng)長(zhǎng)避短無疑是最好的方式。如果你對(duì)此感到無所適從,本文或許可以參考一二。
4、各類消息隊(duì)列簡(jiǎn)述
ActiveMQ 是 Apache 出品的、采用 Java 語言編寫的完全基于 JMS1.1 規(guī)范的面向消息的中間件,為應(yīng)用程序提供高效的、可擴(kuò)展的、穩(wěn)定的和安全的企業(yè)級(jí)消息通信。不過由于歷史原因包袱太重,目前市場(chǎng)份額沒有后面三種消息中間件多,其最新架構(gòu)被命名為 Apollo,號(hào)稱下一代 ActiveMQ,有興趣的同學(xué)可行了解。
RabbitMQ 是采用 Erlang 語言實(shí)現(xiàn)的 AMQP 協(xié)議的消息中間件,最初起源于金融系統(tǒng),用于在分布式系統(tǒng)中存儲(chǔ)轉(zhuǎn)發(fā)消息。RabbitMQ 發(fā)展到今天,被越來越多的人認(rèn)可,這和它在可靠性、可用性、擴(kuò)展性、功能豐富等方面的卓越表現(xiàn)是分不開的。
Kafka 起初是由 LinkedIn 公司采用 Scala 語言開發(fā)的一個(gè)分布式、多分區(qū)、多副本且基于 zookeeper 協(xié)調(diào)的分布式消息系統(tǒng),現(xiàn)已捐獻(xiàn)給 Apache 基金會(huì)。它是一種高吞吐量的分布式發(fā)布訂閱消息系統(tǒng),以可水平擴(kuò)展和高吞吐率而被廣泛使用。目前越來越多的開源分布式處理系統(tǒng)如 Cloudera、Apache Storm、Spark、Flink 等都支持與 Kafka 集成。
RocketMQ 是阿里開源的消息中間件,目前已經(jīng)捐獻(xiàn)個(gè) Apache 基金會(huì),它是由 Java 語言開發(fā)的,具備高吞吐量、高可用性、適合大規(guī)模分布式系統(tǒng)應(yīng)用等特點(diǎn),經(jīng)歷過雙 11 的洗禮,實(shí)力不容小覷。
ZeroMQ 號(hào)稱史上最快的消息隊(duì)列,基于 C 語言開發(fā)。ZeroMQ 是一個(gè)消息處理隊(duì)列庫,可在多線程、多內(nèi)核和主機(jī)之間彈性伸縮,雖然大多數(shù)時(shí)候我們習(xí)慣將其歸入消息隊(duì)列家族之中,但是其和前面的幾款有著本質(zhì)的區(qū)別,ZeroMQ 本身就不是一個(gè)消息隊(duì)列服務(wù)器,更像是一組底層網(wǎng)絡(luò)通訊庫,對(duì)原有的 Socket API 上加上一層封裝而已。
目前市面上的消息中間件還有很多,比如騰訊系的 PhxQueue、CMQ、CKafka,又比如基于 Go 語言的 NSQ,有時(shí)人們也把類似 Redis 的產(chǎn)品也看做消息中間件的一種,當(dāng)然它們都很優(yōu)秀,但是本文篇幅限制無法窮極所有,下面會(huì)針對(duì)性的挑選 RabbitMQ 和 Kafka 兩款典型的消息中間件來做分析,力求站在一個(gè)公平公正的立場(chǎng)來闡述消息中間件選型中的各個(gè)要點(diǎn)。
5、選型要點(diǎn)概述
衡量一款消息中間件是否符合需求需要從多個(gè)維度進(jìn)行考察,首要的就是功能維度,這個(gè)直接決定了你能否最大程度上的實(shí)現(xiàn)開箱即用,進(jìn)而縮短項(xiàng)目周期、降低成本等。如果一款消息中間件的功能達(dá)不到想要的功能,那么就需要進(jìn)行二次開發(fā),這樣會(huì)增加項(xiàng)目的技術(shù)難度、復(fù)雜度以及增大項(xiàng)目周期等。
6、具體技術(shù)選型指標(biāo)1:功能維度
功能維度又可以劃分個(gè)多個(gè)子維度,大致可以分為以下這些。
優(yōu)先級(jí)隊(duì)列:
優(yōu)先級(jí)隊(duì)列不同于先進(jìn)先出隊(duì)列,優(yōu)先級(jí)高的消息具備優(yōu)先被消費(fèi)的特權(quán),這樣可以為下游提供不同消息級(jí)別的保證。不過這個(gè)優(yōu)先級(jí)也是需要有一個(gè)前提的:如果消費(fèi)者的消費(fèi)速度大于生產(chǎn)者的速度,并且消息中間件服務(wù)器(一般簡(jiǎn)單的稱之為 Broker)中沒有消息堆積,那么對(duì)于發(fā)送的消息設(shè)置優(yōu)先級(jí)也就沒有什么實(shí)質(zhì)性的意義了,因?yàn)樯a(chǎn)者剛發(fā)送完一條消息就被消費(fèi)者消費(fèi)了,那么就相當(dāng)于 Broker 中至多只有一條消息,對(duì)于單條消息來說優(yōu)先級(jí)是沒有什么意義的。
延遲隊(duì)列:
當(dāng)你在網(wǎng)上購(gòu)物的時(shí)候是否會(huì)遇到這樣的提示:“三十分鐘之內(nèi)未付款,訂單自動(dòng)取消”?這個(gè)是延遲隊(duì)列的一種典型應(yīng)用場(chǎng)景。延遲隊(duì)列存儲(chǔ)的是對(duì)應(yīng)的延遲消息,所謂“延遲消息”是指當(dāng)消息被發(fā)送以后,并不想讓消費(fèi)者立刻拿到消息,而是等待特定時(shí)間后,消費(fèi)者才能拿到這個(gè)消息進(jìn)行消費(fèi)。延遲隊(duì)列一般分為兩種:基于消息的延遲和基于隊(duì)列的延遲。基于消息的延遲是指為每條消息設(shè)置不同的延遲時(shí)間,那么每當(dāng)隊(duì)列中有新消息進(jìn)入的時(shí)候就會(huì)重新根據(jù)延遲時(shí)間排序,當(dāng)然這也會(huì)對(duì)性能造成極大的影響。實(shí)際應(yīng)用中大多采用基于隊(duì)列的延遲,設(shè)置不同延遲級(jí)別的隊(duì)列,比如 5s、10s、30s、1min、5mins、10mins 等,每個(gè)隊(duì)列中消息的延遲時(shí)間都是相同的,這樣免去了延遲排序所要承受的性能之苦,通過一定的掃描策略(比如定時(shí))即可投遞超時(shí)的消息。
死信隊(duì)列:
由于某些原因消息無法被正確的投遞,為了確保消息不會(huì)被無故的丟棄,一般將其置于一個(gè)特殊角色的隊(duì)列,這個(gè)隊(duì)列一般稱之為死信隊(duì)列。與此對(duì)應(yīng)的還有一個(gè)“回退隊(duì)列”的概念,試想如果消費(fèi)者在消費(fèi)時(shí)發(fā)生了異常,那么就不會(huì)對(duì)這一次消費(fèi)進(jìn)行確認(rèn)(Ack), 進(jìn)而發(fā)生回滾消息的操作之后消息始終會(huì)放在隊(duì)列的頂部,然后不斷被處理和回滾,導(dǎo)致隊(duì)列陷入死循環(huán)。為了解決這個(gè)問題,可以為每個(gè)隊(duì)列設(shè)置一個(gè)回退隊(duì)列,它和死信隊(duì)列都是為異常的處理提供的一種機(jī)制保障。實(shí)際情況下,回退隊(duì)列的角色可以由死信隊(duì)列和重試隊(duì)列來扮演。
重試隊(duì)列:
重試隊(duì)列其實(shí)可以看成是一種回退隊(duì)列,具體指消費(fèi)端消費(fèi)消息失敗時(shí),為防止消息無故丟失而重新將消息回滾到 Broker 中。與回退隊(duì)列不同的是重試隊(duì)列一般分成多個(gè)重試等級(jí),每個(gè)重試等級(jí)一般也會(huì)設(shè)置重新投遞延時(shí),重試次數(shù)越多投遞延時(shí)就越大。舉個(gè)例子:消息第一次消費(fèi)失敗入重試隊(duì)列 Q1,Q1 的重新投遞延遲為 5s,在 5s 過后重新投遞該消息;如果消息再次消費(fèi)失敗則入重試隊(duì)列 Q2,Q2 的重新投遞延遲為 10s,在 10s 過后再次投遞該消息。以此類推,重試越多次重新投遞的時(shí)間就越久,為此需要設(shè)置一個(gè)上限,超過投遞次數(shù)就入死信隊(duì)列。重試隊(duì)列與延遲隊(duì)列有相同的地方,都是需要設(shè)置延遲級(jí)別,它們彼此的區(qū)別是:延遲隊(duì)列動(dòng)作由內(nèi)部觸發(fā),重試隊(duì)列動(dòng)作由外部消費(fèi)端觸發(fā);延遲隊(duì)列作用一次,而重試隊(duì)列的作用范圍會(huì)向后傳遞。
消費(fèi)模式:
消費(fèi)模式分為推(push)模式和拉(pull)模式。推模式是指由 Broker 主動(dòng)推送消息至消費(fèi)端,實(shí)時(shí)性較好,不過需要一定的流制機(jī)制來確保服務(wù)端推送過來的消息不會(huì)壓垮消費(fèi)端。而拉模式是指消費(fèi)端主動(dòng)向 Broker 端請(qǐng)求拉取(一般是定時(shí)或者定量)消息,實(shí)時(shí)性較推模式差,但是可以根據(jù)自身的處理能力而控制拉取的消息量。
廣播消費(fèi):
消息一般有兩種傳遞模式:點(diǎn)對(duì)點(diǎn)(P2P,Point-to-Point)模式和發(fā)布 / 訂閱(Pub/Sub)模式。對(duì)于點(diǎn)對(duì)點(diǎn)的模式而言,消息被消費(fèi)以后,隊(duì)列中不會(huì)再存儲(chǔ),所以消息消費(fèi)者不可能消費(fèi)到已經(jīng)被消費(fèi)的消息。雖然隊(duì)列可以支持多個(gè)消費(fèi)者,但是一條消息只會(huì)被一個(gè)消費(fèi)者消費(fèi)。發(fā)布訂閱模式定義了如何向一個(gè)內(nèi)容節(jié)點(diǎn)發(fā)布和訂閱消息,這個(gè)內(nèi)容節(jié)點(diǎn)稱為主題(topic),主題可以認(rèn)為是消息傳遞的中介,消息發(fā)布者將消息發(fā)布到某個(gè)主題,而消息訂閱者則從主題中訂閱消息。主題使得消息的訂閱者與消息的發(fā)布者互相保持獨(dú)立,不需要進(jìn)行接觸即可保證消息的傳遞,發(fā)布 / 訂閱模式在消息的一對(duì)多廣播時(shí)采用。RabbitMQ 是一種典型的點(diǎn)對(duì)點(diǎn)模式,而 Kafka 是一種典型的發(fā)布訂閱模式。但是 RabbitMQ 中可以通過設(shè)置交換器類型來實(shí)現(xiàn)發(fā)布訂閱模式而達(dá)到廣播消費(fèi)的效果,Kafka 中也能以點(diǎn)對(duì)點(diǎn)的形式消費(fèi),你完全可以把其消費(fèi)組(consumer group)的概念看成是隊(duì)列的概念。不過對(duì)比來說,Kafka 中因?yàn)橛辛讼⒒厮莨δ艿拇嬖冢瑢?duì)于廣播消費(fèi)的力度支持比 RabbitMQ 的要強(qiáng)。
消息回溯:
一般消息在消費(fèi)完成之后就被處理了,之后再也不能消費(fèi)到該條消息。消息回溯正好相反,是指消息在消費(fèi)完成之后,還能消費(fèi)到之前被消費(fèi)掉的消息。對(duì)于消息而言,經(jīng)常面臨的問題是“消息丟失”,至于是真正由于消息中間件的缺陷丟失還是由于使用方的誤用而丟失一般很難追查,如果消息中間件本身具備消息回溯功能的話,可以通過回溯消費(fèi)復(fù)現(xiàn)“丟失的”消息進(jìn)而查出問題的源頭之所在。消息回溯的作用遠(yuǎn)不止與此,比如還有索引恢復(fù)、本地緩存重建,有些業(yè)務(wù)補(bǔ)償方案也可以采用回溯的方式來實(shí)現(xiàn)。
消息堆積 + 持久化:
流量削峰是消息中間件的一個(gè)非常重要的功能,而這個(gè)功能其實(shí)得益于其消息堆積能力。從某種意義上來講,如果一個(gè)消息中間件不具備消息堆積的能力,那么就不能把它看做是一個(gè)合格的消息中間件。消息堆積分內(nèi)存式堆積和磁盤式堆積。RabbitMQ 是典型的內(nèi)存式堆積,但這并非絕對(duì),在某些條件觸發(fā)后會(huì)有換頁動(dòng)作來將內(nèi)存中的消息換頁到磁盤(換頁動(dòng)作會(huì)影響吞吐),或者直接使用惰性隊(duì)列來將消息直接持久化至磁盤中。Kafka 是一種典型的磁盤式堆積,所有的消息都存儲(chǔ)在磁盤中。一般來說,磁盤的容量會(huì)比內(nèi)存的容量要大得多,對(duì)于磁盤式的堆積其堆積能力就是整個(gè)磁盤的大小。從另外一個(gè)角度講,消息堆積也為消息中間件提供了冗余存儲(chǔ)的功能。援引 紐約時(shí)報(bào)的案例,其直接將 Kafka 用作存儲(chǔ)系統(tǒng)。
消息追蹤:
對(duì)于分布式架構(gòu)系統(tǒng)中的鏈路追蹤(trace)而言,大家一定不會(huì)陌生。對(duì)于消息中間件而言,消息的鏈路追蹤(以下簡(jiǎn)稱消息追蹤)同樣重要。對(duì)于消息追蹤最通俗的理解就是要知道消息從哪來,存在哪里以及發(fā)往哪里去。基于此功能下,我們可以對(duì)發(fā)送或者消費(fèi)完的消息進(jìn)行鏈路追蹤服務(wù),進(jìn)而可以進(jìn)行問題的快速定位與排查。
消息過濾:
消息過濾是指按照既定的過濾規(guī)則為下游用戶提供指定類別的消息。就以 kafka 而言,完全可以將不同類別的消息發(fā)送至不同的 topic 中,由此可以實(shí)現(xiàn)某種意義的消息過濾,或者 Kafka 還可以根據(jù)分區(qū)對(duì)同一個(gè) topic 中的消息進(jìn)行分類。不過更加嚴(yán)格意義上的消息過濾應(yīng)該是對(duì)既定的消息采取一定的方式按照一定的過濾規(guī)則進(jìn)行過濾。同樣以 Kafka 為例,可以通過客戶端提供的 ConsumerInterceptor 接口或者 Kafka Stream 的 filter 功能進(jìn)行消息過濾。
多租戶:
也可以稱為多重租賃技術(shù),是一種軟件架構(gòu)技術(shù),主要用來實(shí)現(xiàn)多用戶的環(huán)境下公用相同的系統(tǒng)或程序組件,并且仍可以確保各用戶間數(shù)據(jù)的隔離性。RabbitMQ 就能夠支持多租戶技術(shù),每一個(gè)租戶表示為一個(gè) vhost,其本質(zhì)上是一個(gè)獨(dú)立的小型 RabbitMQ 服務(wù)器,又有自己獨(dú)立的隊(duì)列、交換器及綁定關(guān)系等,并且它擁有自己獨(dú)立的權(quán)限。vhost 就像是物理機(jī)中的虛擬機(jī)一樣,它們?cè)诟鱾€(gè)實(shí)例間提供邏輯上的分離,為不同程序安全保密地允許數(shù)據(jù),它既能將同一個(gè) RabbitMQ 中的眾多客戶區(qū)分開,又可以避免隊(duì)列和交換器等命名沖突。
多協(xié)議支持:
消息是信息的載體,為了讓生產(chǎn)者和消費(fèi)者都能理解所承載的信息(生產(chǎn)者需要知道如何構(gòu)造消息,消費(fèi)者需要知道如何解析消息),它們就需要按照一種統(tǒng)一的格式描述消息,這種統(tǒng)一的格式稱之為消息協(xié)議。有效的消息一定具有某種格式,而沒有格式的消息是沒有意義的。一般消息層面的協(xié)議有 AMQP、MQTT、STOMP、XMPP 等(消息領(lǐng)域中的 JMS 更多的是一個(gè)規(guī)范而不是一個(gè)協(xié)議),支持的協(xié)議越多其應(yīng)用范圍就會(huì)越廣,通用性越強(qiáng),比如 RabbitMQ 能夠支持 MQTT 協(xié)議就讓其在物聯(lián)網(wǎng)應(yīng)用中獲得一席之地。還有的消息中間件是基于其本身的私有協(xié)議運(yùn)轉(zhuǎn)的,典型的如 Kafka。
跨語言支持:
對(duì)很多公司而言,其技術(shù)棧體系中會(huì)有多種編程語言,如 C/C++、JAVA、Go、PHP 等,消息中間件本身具備應(yīng)用解耦的特性,如果能夠進(jìn)一步的支持多客戶端語言,那么就可以將此特性的效能擴(kuò)大。跨語言的支持力度也可以從側(cè)面反映出一個(gè)消息中間件的流行程度。
流量控制:
流量控制(flow control)針對(duì)的是發(fā)送方和接收方速度不匹配的問題,提供一種速度匹配服務(wù)抑制發(fā)送速率使接收方應(yīng)用程序的讀取速率與之相適應(yīng)。通常的流控方法有 Stop-and-wait、滑動(dòng)窗口以及令牌桶等。
消息順序性:
顧名思義,消息順序性是指保證消息有序。這個(gè)功能有個(gè)很常見的應(yīng)用場(chǎng)景就是 CDC(Change Data Chapture),以 MySQL 為例,如果其傳輸?shù)?binlog 的順序出錯(cuò),比如原本是先對(duì)一條數(shù)據(jù)加 1,然后再乘以 2,發(fā)送錯(cuò)序之后就變成了先乘以 2 后加 1 了,造成了數(shù)據(jù)不一致。
安全機(jī)制:
在 Kafka 0.9 版本之后就開始增加了身份認(rèn)證和權(quán)限控制兩種安全機(jī)制。身份認(rèn)證是指客戶端與服務(wù)端連接進(jìn)行身份認(rèn)證,包括客戶端與 Broker 之間、Broker 與 Broker 之間、Broker 與 ZooKeeper 之間的連接認(rèn)證,目前支持 SSL、SASL 等認(rèn)證機(jī)制。權(quán)限控制是指對(duì)客戶端的讀寫操作進(jìn)行權(quán)限控制,包括對(duì)消息或 Kafka 集群操作權(quán)限控制。權(quán)限控制是可插拔的,并支持與外部的授權(quán)服務(wù)進(jìn)行集成。對(duì)于 RabbitMQ 而言,其同樣提供身份認(rèn)證(TLS/SSL、SASL)和權(quán)限控制(讀寫操作)的安全機(jī)制。
消息冪等性:
對(duì)于確保消息在生產(chǎn)者和消費(fèi)者之間進(jìn)行傳輸而言一般有三種傳輸保障(delivery guarantee):At most once,至多一次,消息可能丟失,但絕不會(huì)重復(fù)傳輸;At least once,至少一次,消息絕不會(huì)丟,但是可能會(huì)重復(fù);Exactly once,精確一次,每條消息肯定會(huì)被傳輸一次且僅一次。對(duì)于大多數(shù)消息中間件而言,一般只提供 At most once 和 At least once 兩種傳輸保障,對(duì)于第三種一般很難做到,由此消息冪等性也很難保證。
Kafka 自 0.11 版本開始引入了冪等性和事務(wù),Kafka 的冪等性是指單個(gè)生產(chǎn)者對(duì)于單分區(qū)單會(huì)話的冪等,而事務(wù)可以保證原子性地寫入到多個(gè)分區(qū),即寫入到多個(gè)分區(qū)的消息要么全部成功,要么全部回滾,這兩個(gè)功能加起來可以讓 Kafka 具備 EOS(Exactly Once Semantic)的能力。
不過如果要考慮全局的冪等,還需要與從上下游方面綜合考慮,即關(guān)聯(lián)業(yè)務(wù)層面,冪等處理本身也是業(yè)務(wù)層面所需要考慮的重要議題。以下游消費(fèi)者層面為例,有可能消費(fèi)者消費(fèi)完一條消息之后沒有來得及確認(rèn)消息就發(fā)生異常,等到恢復(fù)之后又得重新消費(fèi)原來消費(fèi)過的那條消息,那么這種類型的消息冪等是無法有消息中間件層面來保證的。如果要保證全局的冪等,需要引入更多的外部資源來保證,比如以訂單號(hào)作為唯一性標(biāo)識(shí),并且在下游設(shè)置一個(gè)去重表。
事務(wù)性消息:
事務(wù)本身是一個(gè)并不陌生的詞匯,事務(wù)是由事務(wù)開始(Begin Transaction)和事務(wù)結(jié)束(End Transaction)之間執(zhí)行的全體操作組成。支持事務(wù)的消息中間件并不在少數(shù),Kafka 和 RabbitMQ 都支持,不過此兩者的事務(wù)是指生產(chǎn)者發(fā)生消息的事務(wù),要么發(fā)送成功,要么發(fā)送失敗。消息中間件可以作為用來實(shí)現(xiàn)分布式事務(wù)的一種手段,但其本身并不提供全局分布式事務(wù)的功能。
下表是對(duì) Kafka 與 RabbitMQ 功能的總結(jié)性對(duì)比及補(bǔ)充說明:
7、具體技術(shù)選型指標(biāo)2:性能
功能維度是消息中間件選型中的一個(gè)重要的參考維度,但這并不是唯一的維度。有時(shí)候性能比功能還要重要,況且性能和功能很多時(shí)候是相悖的,魚和熊掌不可兼得,Kafka 在開啟冪等、事務(wù)功能的時(shí)候會(huì)使其性能降低,RabbitMQ 在開啟 rabbitmq_tracing 插件的時(shí)候也會(huì)極大的影響其性能。消息中間件的性能一般是指其吞吐量,雖然從功能維度上來說,RabbitMQ 的優(yōu)勢(shì)要大于 Kafka,但是 Kafka 的吞吐量要比 RabbitMQ 高出 1 至 2 個(gè)數(shù)量級(jí),一般 RabbitMQ 的單機(jī) QPS 在萬級(jí)別之內(nèi),而 Kafka 的單機(jī) QPS 可以維持在十萬級(jí)別,甚至可以達(dá)到百萬級(jí)。
消息中間件的吞吐量始終會(huì)受到硬件層面的限制。就以網(wǎng)卡帶寬為例,如果單機(jī)單網(wǎng)卡的帶寬為 1Gbps,如果要達(dá)到百萬級(jí)的吞吐,那么消息體大小不得超過 (1Gb/8)/100W,即約等于 134B,換句話說如果消息體大小超過 134B,那么就不可能達(dá)到百萬級(jí)別的吞吐。這種計(jì)算方式同樣可以適用于內(nèi)存和磁盤。
時(shí)延作為性能維度的一個(gè)重要指標(biāo),卻往往在消息中間件領(lǐng)域所被忽視,因?yàn)橐话闶褂孟⒅虚g件的場(chǎng)景對(duì)時(shí)效性的要求并不是很高,如果要求時(shí)效性完全可以采用 RPC 的方式實(shí)現(xiàn)。消息中間件具備消息堆積的能力,消息堆積越大也就意味著端到端的時(shí)延也就越長(zhǎng),與此同時(shí)延時(shí)隊(duì)列也是某些消息中間件的一大特色。那么為什么還要關(guān)注消息中間件的時(shí)延問題呢?消息中間件能夠解耦系統(tǒng),對(duì)于一個(gè)時(shí)延較低的消息中間件而言,它可以讓上游生產(chǎn)者發(fā)送消息之后可以迅速的返回,也可以讓消費(fèi)者更加快速的獲取到消息,在沒有堆積的情況下可以讓整體上下游的應(yīng)用之間的級(jí)聯(lián)動(dòng)作更加高效,雖然不建議在時(shí)效性很高的場(chǎng)景下使用消息中間件,但是如果所使用的消息中間件的時(shí)延方面比較優(yōu)秀,那么對(duì)于整體系統(tǒng)的性能將會(huì)是一個(gè)不小的提升。
8、具體技術(shù)選型指標(biāo)3:可靠性 + 可用性
消息丟失是使用消息中間件時(shí)所不得不面對(duì)的一個(gè)同點(diǎn),其背后消息可靠性也是衡量消息中間件好壞的一個(gè)關(guān)鍵因素。尤其是在金融支付領(lǐng)域,消息可靠性尤為重要。然而說到可靠性必然要說到可用性,注意這兩者之間的區(qū)別,消息中間件的可靠性是指對(duì)消息不丟失的保障程度;而消息中間件的可用性是指無故障運(yùn)行的時(shí)間百分比,通常用幾個(gè) 9 來衡量。
從狹義的角度來說,分布式系統(tǒng)架構(gòu)是一致性協(xié)議理論的應(yīng)用實(shí)現(xiàn),對(duì)于消息可靠性和可用性而言也可以追溯到消息中間件背后的一致性協(xié)議。對(duì)于 Kafka 而言,其采用的是類似 PacificA 的一致性協(xié)議,通過 ISR(In-Sync-Replica)來保證多副本之間的同步,并且支持強(qiáng)一致性語義(通過 acks 實(shí)現(xiàn))。對(duì)應(yīng)的 RabbitMQ 是通過鏡像環(huán)形隊(duì)列實(shí)現(xiàn)多副本及強(qiáng)一致性語義的。多副本可以保證在 master 節(jié)點(diǎn)宕機(jī)異常之后可以提升 slave 作為新的 master 而繼續(xù)提供服務(wù)來保障可用性。Kafka 設(shè)計(jì)之初是為日志處理而生,給人們留下了數(shù)據(jù)可靠性要求不要的不良印象,但是隨著版本的升級(jí)優(yōu)化,其可靠性得到極大的增強(qiáng),詳細(xì)可以參考 KIP101。就目前而言,在金融支付領(lǐng)域使用 RabbitMQ 居多,而在日志處理、大數(shù)據(jù)等方面 Kafka 使用居多,隨著 RabbitMQ 性能的不斷提升和 Kafka 可靠性的進(jìn)一步增強(qiáng),相信彼此都能在以前不擅長(zhǎng)的領(lǐng)域分得一杯羹。
同步刷盤是增強(qiáng)一個(gè)組件可靠性的有效方式,消息中間件也不例外,Kafka 和 RabbitMQ 都可以支持同步刷盤,但是筆者對(duì)同步刷盤有一定的疑問:絕大多數(shù)情景下,一個(gè)組件的可靠性不應(yīng)該由同步刷盤這種極其損耗性能的操作來保障,而是采用多副本的機(jī)制來保證。
這里還要提及的一個(gè)方面是擴(kuò)展能力,這里我狹隘地將此歸納到可用性這一維度,消息中間件的擴(kuò)展能力能夠增強(qiáng)其用可用能力及范圍,比如前面提到的 RabbitMQ 支持多種消息協(xié)議,這個(gè)就是基于其插件化的擴(kuò)展實(shí)現(xiàn)。還有從集群部署上來講,歸功于 Kafka 的水平擴(kuò)展能力,其基本上可以達(dá)到線性容量提升的水平,在 LinkedIn 實(shí)踐介紹中就提及了有部署超過千臺(tái)設(shè)備的 Kafka 集群。
9、具體技術(shù)選型指標(biāo)4:運(yùn)維管理
在消息中間件的使用過程中難免會(huì)出現(xiàn)各式各樣的異常情況,有客戶端的,也有服務(wù)端的,那么怎樣及時(shí)有效的進(jìn)行監(jiān)測(cè)及修復(fù)。業(yè)務(wù)線流量有峰值又低谷,尤其是電商領(lǐng)域,那么怎樣前進(jìn)行有效的容量評(píng)估,尤其是大促期間?腳踢電源、網(wǎng)線被挖等事件層出不窮,如何有效的做好異地多活?這些都離不開消息中間件的衍生產(chǎn)品——運(yùn)維管理。
運(yùn)維管理也可以進(jìn)行進(jìn)一步的細(xì)分,比如:申請(qǐng)、審核、監(jiān)控、告警、管理、容災(zāi)、部署等。
申請(qǐng)、審核很好理解,在源頭對(duì)資源進(jìn)行管控,既可以進(jìn)行有效校正應(yīng)用方的使用規(guī)范,配和監(jiān)控也可以做好流量統(tǒng)計(jì)與流量評(píng)估工作,一般申請(qǐng)、審核與公司內(nèi)部系統(tǒng)交融性較大,不適合使用開源類的產(chǎn)品。
監(jiān)控、告警也比較好理解,對(duì)消息中間件的使用進(jìn)行全方位的監(jiān)控,即可以為系統(tǒng)提供基準(zhǔn)數(shù)據(jù),也可以在檢測(cè)到異常的情況配合告警,以便運(yùn)維、開發(fā)人員的迅速介入。除了一般的監(jiān)控項(xiàng)(比如硬件、GC 等)之外,對(duì)于消息中間件還需要關(guān)注端到端時(shí)延、消息審計(jì)、消息堆積等方面。對(duì)于 RabbitMQ 而言,最正統(tǒng)的監(jiān)控管理工具莫過于 rabbitmq_management 插件了,但是社區(qū)內(nèi)還有 AppDynamics, Collectd, DataDog, Ganglia, Munin, Nagios, New Relic, Prometheus, Zenoss 等多種優(yōu)秀的產(chǎn)品。Kafka 在此方面也毫不遜色,比如:Kafka Manager, Kafka Monitor, Kafka Offset Monitor, Burrow, Chaperone, Confluent Control Center 等產(chǎn)品,尤其是 Cruise 還可以提供自動(dòng)化運(yùn)維的功能。
不管是擴(kuò)容、降級(jí)、版本升級(jí)、集群節(jié)點(diǎn)部署、還是故障處理都離不開管理工具的應(yīng)用,一個(gè)配套完備的管理工具集可以在遇到變更時(shí)做到事半功倍。故障可大可小,一般是一些應(yīng)用異常,也可以是機(jī)器掉電、網(wǎng)絡(luò)異常、磁盤損壞等單機(jī)故障,這些故障單機(jī)房?jī)?nèi)的多副本足以應(yīng)付。如果是機(jī)房故障就要涉及異地容災(zāi)了,關(guān)鍵點(diǎn)在于如何有效的進(jìn)行數(shù)據(jù)復(fù)制,對(duì)于 Kafka 而言,可以參考 MirrorMarker、uReplicator 等產(chǎn)品,而 RabbitMQ 可以參考 Federation 和 Shovel。
10、具體技術(shù)選型指標(biāo)5:社區(qū)力度及生態(tài)發(fā)展
對(duì)于目前流行的編程語言而言,如 Java、Python,如果你在使用過程中遇到了一些異常,基本上可以通過搜索引擎的幫助來得到解決,因?yàn)橐粋€(gè)產(chǎn)品用的人越多,踩過的坑也就越多,對(duì)應(yīng)的解決方案也就越多。對(duì)于消息中間件也同樣適用,如果你選擇了一種“生僻”的消息中間件,可能在某些方面運(yùn)用的得心應(yīng)手,但是版本更新緩慢、遇到棘手問題也難以得到社區(qū)的支持而越陷越深;相反如果你選擇了一種“流行”的消息中間件,其更新力度大,不僅可以迅速的彌補(bǔ)之前的不足,而且也能順應(yīng)技術(shù)的快速發(fā)展來變更一些新的功能,這樣可以讓你以“站在巨人的肩膀上”。在運(yùn)維管理維度我們提及了 Kafka 和 RabbitMQ 都有一系列開源的監(jiān)控管理產(chǎn)品,這些正是得益于其社區(qū)及生態(tài)的迅猛發(fā)展。
11、消息中間件選型誤區(qū)總結(jié)
在進(jìn)行消息中間件選型之前可以先問自己一個(gè)問題:是否真的需要一個(gè)消息中間件?在搞清楚這個(gè)問題之后,還可以繼續(xù)問自己一個(gè)問題:是否需要自己維護(hù)一套消息中間件?很多初創(chuàng)型公司為了節(jié)省成本會(huì)選擇直接購(gòu)買消息中間件有關(guān)的云服務(wù),自己只需要關(guān)注收發(fā)消息即可,其余的都可以外包出去。
很多人面對(duì)消息中間件時(shí)會(huì)有一種自研的沖動(dòng),你完全可以對(duì) Java 中的 ArrayBlockingQueue 做一個(gè)簡(jiǎn)單的封裝,你也可以基于文件、數(shù)據(jù)庫、Redis 等底層存儲(chǔ)封裝而形成一個(gè)消息中間件。消息中間件做為一個(gè)基礎(chǔ)組件并沒有想象中的那么簡(jiǎn)單,其背后還需要配套的管理運(yùn)維整個(gè)生態(tài)的產(chǎn)品集。自研還有會(huì)交接問題,如果文檔不齊全、運(yùn)作不規(guī)范將會(huì)帶給新人噩夢(mèng)般的體驗(yàn)。是否真的有自研的必要?如果不是 KPI 的壓迫可以先考慮下這 2 個(gè)問題:1. 目前市面上的消息中間件是否都真的無法滿足目前業(yè)務(wù)需求? 2. 團(tuán)隊(duì)是否有足夠的能力、人力、財(cái)力、精力來支持自研?
很多人在做消息中間件選型時(shí)會(huì)參考網(wǎng)絡(luò)上的很多對(duì)比類的文章,但是其專業(yè)性、嚴(yán)謹(jǐn)性、以及其政治立場(chǎng)問題都有待考證,需要帶著懷疑的態(tài)度去審視這些文章。比如有些文章會(huì)在沒有任何限定條件及場(chǎng)景的情況下直接定義某款消息中間件最好,還有些文章沒有指明消息中間件版本及測(cè)試環(huán)境就來做功能和性能對(duì)比分析,諸如此類的文章都可以唾棄之。
消息中間件猶如小馬過河,選擇合適的才最重要,這需要貼合自身的業(yè)務(wù)需求,技術(shù)服務(wù)于業(yè)務(wù),大體上可以根據(jù)上一節(jié)所提及的功能、性能等 6 個(gè)維度來一一進(jìn)行篩選。更深層次的抉擇在于你能否掌握其魂,筆者鄙見:RabbitMQ 在于 routing,而 Kafka 在于 streaming,了解其根本對(duì)于自己能夠?qū)ΠY下藥選擇到合適的消息中間件尤為重要。
消息中間件選型切忌一味的追求性能或者功能,性能可以優(yōu)化,功能可以二次開發(fā)。如果要在功能和性能方面做一個(gè)抉擇的話,那么首選性能,因?yàn)榭傮w上來說性能優(yōu)化的空間沒有功能擴(kuò)展的空間大。然而對(duì)于長(zhǎng)期發(fā)展而言,生態(tài)又比性能以及功能都要重要。
很多時(shí)候,對(duì)于可靠性方面也容易存在一個(gè)誤區(qū):想要找到一個(gè)產(chǎn)品來保證消息的絕對(duì)可靠,很不幸的是這世界上沒有絕對(duì)的東西,只能說盡量趨于完美。想要盡可能的保障消息的可靠性也并非單單只靠消息中間件本身,還要依賴于上下游,需要從生產(chǎn)端、服務(wù)端和消費(fèi)端這 3 個(gè)維度去努力保證,《RabbitMQ 消息可靠性分析》這篇文章就從這 3 個(gè)維度去分析了 RabbitMQ 的可靠性。
消息中間件選型還有一個(gè)考量標(biāo)準(zhǔn)就是盡量貼合團(tuán)隊(duì)自身的技術(shù)棧體系,雖然說沒有蹩腳的消息中間件只有蹩腳的程序員,但是讓一個(gè) C 棧的團(tuán)隊(duì)去深挖 PhxQueue 總比去深挖 Scala 編寫的 Kafka 要容易的多。
消息中間件大道至簡(jiǎn):一發(fā)一存一消費(fèi),沒有最好的消息中間件,只有最合適的消息中間件。
附錄:更多相關(guān)技術(shù)文章
[1] 有關(guān)IM架構(gòu)設(shè)計(jì)方面:
《淺談IM系統(tǒng)的架構(gòu)設(shè)計(jì)》
《簡(jiǎn)述移動(dòng)端IM開發(fā)的那些坑:架構(gòu)設(shè)計(jì)、通信協(xié)議和客戶端》
《一套海量在線用戶的移動(dòng)端IM架構(gòu)設(shè)計(jì)實(shí)踐分享(含詳細(xì)圖文)》
《一套原創(chuàng)分布式即時(shí)通訊(IM)系統(tǒng)理論架構(gòu)方案》
《從零到卓越:京東客服即時(shí)通訊系統(tǒng)的技術(shù)架構(gòu)演進(jìn)歷程》
《蘑菇街即時(shí)通訊/IM服務(wù)器開發(fā)之架構(gòu)選擇》
《騰訊QQ1.4億在線用戶的技術(shù)挑戰(zhàn)和架構(gòu)演進(jìn)之路PPT》
《微信后臺(tái)基于時(shí)間序的海量數(shù)據(jù)冷熱分級(jí)架構(gòu)設(shè)計(jì)實(shí)踐》
《微信技術(shù)總監(jiān)談架構(gòu):微信之道——大道至簡(jiǎn)(演講全文)》
《如何解讀《微信技術(shù)總監(jiān)談架構(gòu):微信之道——大道至簡(jiǎn)》》
《快速裂變:見證微信強(qiáng)大后臺(tái)架構(gòu)從0到1的演進(jìn)歷程(一)》
《17年的實(shí)踐:騰訊海量產(chǎn)品的技術(shù)方法論》
《移動(dòng)端IM中大規(guī)模群消息的推送如何保證效率、實(shí)時(shí)性?》
《現(xiàn)代IM系統(tǒng)中聊天消息的同步和存儲(chǔ)方案探討》
《IM開發(fā)基礎(chǔ)知識(shí)補(bǔ)課(二):如何設(shè)計(jì)大量圖片文件的服務(wù)端存儲(chǔ)架構(gòu)?》
《IM開發(fā)基礎(chǔ)知識(shí)補(bǔ)課(三):快速理解服務(wù)端數(shù)據(jù)庫讀寫分離原理及實(shí)踐建議》
《IM開發(fā)基礎(chǔ)知識(shí)補(bǔ)課(四):正確理解HTTP短連接中的Cookie、Session和Token》
《WhatsApp技術(shù)實(shí)踐分享:32人工程團(tuán)隊(duì)創(chuàng)造的技術(shù)神話》
《微信朋友圈千億訪問量背后的技術(shù)挑戰(zhàn)和實(shí)踐總結(jié)》
《王者榮耀2億用戶量的背后:產(chǎn)品定位、技術(shù)架構(gòu)、網(wǎng)絡(luò)方案等》
《IM系統(tǒng)的MQ消息中間件選型:Kafka還是RabbitMQ?》
>> 更多同類文章 ……
[2] IM開發(fā)熱點(diǎn)問題文章:
《移動(dòng)端IM開發(fā)者必讀(一):通俗易懂,理解移動(dòng)網(wǎng)絡(luò)的“弱”和“慢”》
《移動(dòng)端IM開發(fā)者必讀(二):史上最全移動(dòng)弱網(wǎng)絡(luò)優(yōu)化方法總結(jié)》
《從客戶端的角度來談?wù)勔苿?dòng)端IM的消息可靠性和送達(dá)機(jī)制》
《現(xiàn)代移動(dòng)端網(wǎng)絡(luò)短連接的優(yōu)化手段總結(jié):請(qǐng)求速度、弱網(wǎng)適應(yīng)、安全保障》
《騰訊技術(shù)分享:社交網(wǎng)絡(luò)圖片的帶寬壓縮技術(shù)演進(jìn)之路》
《IM開發(fā)基礎(chǔ)知識(shí)補(bǔ)課:正確理解前置HTTP SSO單點(diǎn)登陸接口的原理》
《移動(dòng)端IM中大規(guī)模群消息的推送如何保證效率、實(shí)時(shí)性?》
《移動(dòng)端IM開發(fā)需要面對(duì)的技術(shù)問題》
《開發(fā)IM是自己設(shè)計(jì)協(xié)議用字節(jié)流好還是字符流好?》
《請(qǐng)問有人知道語音留言聊天的主流實(shí)現(xiàn)方式嗎?》
《IM消息送達(dá)保證機(jī)制實(shí)現(xiàn)(一):保證在線實(shí)時(shí)消息的可靠投遞》
《IM消息送達(dá)保證機(jī)制實(shí)現(xiàn)(二):保證離線消息的可靠投遞》
《如何保證IM實(shí)時(shí)消息的“時(shí)序性”與“一致性”?》
《IM單聊和群聊中的在線狀態(tài)同步應(yīng)該用“推”還是“拉”?》
《談?wù)勔苿?dòng)端 IM 開發(fā)中登錄請(qǐng)求的優(yōu)化》
《移動(dòng)端IM登錄時(shí)拉取數(shù)據(jù)如何作到省流量?》
《淺談移動(dòng)端IM的多點(diǎn)登陸和消息漫游原理》
《完全自已開發(fā)的IM該如何設(shè)計(jì)“失敗重試”機(jī)制?》
《通俗易懂:基于集群的移動(dòng)端IM接入層負(fù)載均衡方案分享》
《微信對(duì)網(wǎng)絡(luò)影響的技術(shù)試驗(yàn)及分析(論文全文)》
《即時(shí)通訊系統(tǒng)的原理、技術(shù)和應(yīng)用(技術(shù)論文)》
《開源IM工程“蘑菇街TeamTalk”的現(xiàn)狀:一場(chǎng)有始無終的開源秀》
《QQ音樂團(tuán)隊(duì)分享:Android中的圖片壓縮技術(shù)詳解(上篇)》
《QQ音樂團(tuán)隊(duì)分享:Android中的圖片壓縮技術(shù)詳解(下篇)》
《騰訊原創(chuàng)分享(一):如何大幅提升移動(dòng)網(wǎng)絡(luò)下手機(jī)QQ的圖片傳輸速度和成功率》
《騰訊原創(chuàng)分享(二):如何大幅壓縮移動(dòng)網(wǎng)絡(luò)下APP的流量消耗(上篇)》
《騰訊原創(chuàng)分享(三):如何大幅壓縮移動(dòng)網(wǎng)絡(luò)下APP的流量消耗(下篇)》
《如約而至:微信自用的移動(dòng)端IM網(wǎng)絡(luò)層跨平臺(tái)組件庫Mars已正式開源》
《基于社交網(wǎng)絡(luò)的Yelp是如何實(shí)現(xiàn)海量用戶圖片的無損壓縮的?》
《騰訊技術(shù)分享:騰訊是如何大幅降低帶寬和網(wǎng)絡(luò)流量的(圖片壓縮篇)》
《騰訊技術(shù)分享:騰訊是如何大幅降低帶寬和網(wǎng)絡(luò)流量的(音視頻技術(shù)篇)》
《為什么說即時(shí)通訊社交APP創(chuàng)業(yè)就是一個(gè)坑?》
>> 更多同類文章 ……
(本文同步發(fā)布于:http://www.52im.net/thread-1647-1-1.html)
作者:Jack Jiang (點(diǎn)擊作者姓名進(jìn)入Github)
出處:http://www.52im.net/space-uid-1.html
交流:歡迎加入即時(shí)通訊開發(fā)交流群 215891622
討論:http://www.52im.net/
Jack Jiang同時(shí)是【原創(chuàng)Java
Swing外觀工程BeautyEye】和【輕量級(jí)移動(dòng)端即時(shí)通訊框架MobileIMSDK】的作者,可前往下載交流。
本博文
歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明出處(也可前往 我的52im.net 找到我)。