IM系統的MQ訊息中介軟體選型:Kafka還是RabbitMQ?

IT技術精選文摘發表於2018-06-09

640?wx_fmt=gif

1、前言


在IM這種講究高併發、高訊息吞吐的網際網路場景下,MQ訊息中介軟體是個很重要的基礎設施,它在IM系統的服務端架構中擔當訊息中轉、訊息削峰、訊息交換非同步化等等角色,當然MQ訊息中介軟體的作用遠不止於此,它的價值不僅僅存在於技術上,更重要的是改變了以往同步處理訊息的思路(比如進行IM訊息歷史儲存時,傳統的資訊系統作法可能是收到一條訊息就馬上同步存入資料庫,這種作法在小併發量的情況下可以很好的工作,但網際網路大併發環境下就是災難)。

640?wx_fmt=jpeg

MQ訊息中介軟體可以理解一個水池,水池的這頭是訊息生產者,水池的那頭是訊息消費者,生產者和訊息者無需直接對接,這將帶來很多好處:業務解耦、架構分散式化等,生產者和消費者互相完全透明。

但市面上的MQ訊息中介軟體產品很多,作為IM系統中必不可少的一環,我們該如何選型?那麼請繼續閱讀本文。

2、內容概述


訊息佇列中介軟體(簡稱訊息中介軟體)是指利用高效可靠的訊息傳遞機制進行與平臺無關的資料交流,並基於資料通訊來進行分散式系統的整合。通過提供訊息傳遞和訊息排隊模型,它可以在分散式環境下提供應用解耦、彈性伸縮、冗餘儲存、流量削峰、非同步通訊、資料同步等等功能,其作為分散式系統架構中的一個重要元件,有著舉足輕重的地位。

640?wx_fmt=jpeg

目前開源的訊息中介軟體可謂是琳琅滿目,能讓大家耳熟能詳的就有很多,比如 ActiveMQ、RabbitMQ、Kafka、RocketMQ、ZeroMQ 等。不管選擇其中的哪一款,都會有用的不趁手的地方,畢竟不是為你量身定製的。有些大廠在長期的使用過程中積累了一定的經驗,其訊息佇列的使用場景也相對穩定固化,或者目前市面上的訊息中介軟體無法滿足自身需求,並且也具備足夠的精力和人力而選擇自研來為自己量身打造一款訊息中介軟體。但是絕大多數公司還是不會選擇重複造輪子,那麼選擇一款合適自己的訊息中介軟體顯得尤為重要。就算是前者,那麼在自研出穩定且可靠的相關產品之前還是會經歷這樣一個選型過程。

在整體架構中引入訊息中介軟體,勢必要考慮很多因素,比如成本及收益問題,怎麼樣才能達到最優的價效比?雖然訊息中介軟體種類繁多,但是各自都有各自的側重點,選擇合適自己、揚長避短無疑是最好的方式。如果你對此感到無所適從,本文或許可以參考一二。

4、各類訊息佇列簡述


640?wx_fmt=png

RocketMQ 是阿里開源的訊息中介軟體,目前已經捐獻個 Apache 基金會,它是由 Java 語言開發的,具備高吞吐量、高可用性、適合大規模分散式系統應用等特點,經歷過雙 11 的洗禮,實力不容小覷。

ZeroMQ 號稱史上最快的訊息佇列,基於 C 語言開發。ZeroMQ 是一個訊息處理佇列庫,可在多執行緒、多核心和主機之間彈性伸縮,雖然大多數時候我們習慣將其歸入訊息佇列家族之中,但是其和前面的幾款有著本質的區別,ZeroMQ 本身就不是一個訊息佇列伺服器,更像是一組底層網路通訊庫,對原有的 Socket API 上加上一層封裝而已。

目前市面上的訊息中介軟體還有很多,比如騰訊系的 PhxQueue、CMQ、CKafka,又比如基於 Go 語言的 NSQ,有時人們也把類似 Redis 的產品也看做訊息中介軟體的一種,當然它們都很優秀,但是本文篇幅限制無法窮極所有,下面會針對性的挑選 RabbitMQ 和 Kafka 兩款典型的訊息中介軟體來做分析,力求站在一個公平公正的立場來闡述訊息中介軟體選型中的各個要點。

5、選型要點概述


衡量一款訊息中介軟體是否符合需求需要從多個維度進行考察,首要的就是功能維度,這個直接決定了你能否最大程度上的實現開箱即用,進而縮短專案週期、降低成本等。如果一款訊息中介軟體的功能達不到想要的功能,那麼就需要進行二次開發,這樣會增加專案的技術難度、複雜度以及增大專案週期等。

6、具體技術選型指標1:功能維度


功能維度又可以劃分個多個子維度,大致可以分為以下這些。

640?wx_fmt=png

死信佇列:

由於某些原因訊息無法被正確的投遞,為了確保訊息不會被無故的丟棄,一般將其置於一個特殊角色的佇列,這個佇列一般稱之為死信佇列。與此對應的還有一個“回退佇列”的概念,試想如果消費者在消費時發生了異常,那麼就不會對這一次消費進行確認(Ack), 進而發生回滾訊息的操作之後訊息始終會放在佇列的頂部,然後不斷被處理和回滾,導致佇列陷入死迴圈。為了解決這個問題,可以為每個佇列設定一個回退佇列,它和死信佇列都是為異常的處理提供的一種機制保障。實際情況下,回退佇列的角色可以由死信佇列和重試佇列來扮演。

重試佇列:

重試佇列其實可以看成是一種回退佇列,具體指消費端消費訊息失敗時,為防止訊息無故丟失而重新將訊息回滾到 Broker 中。與回退佇列不同的是重試佇列一般分成多個重試等級,每個重試等級一般也會設定重新投遞延時,重試次數越多投遞延時就越大。舉個例子:訊息第一次消費失敗入重試佇列 Q1,Q1 的重新投遞延遲為 5s,在 5s 過後重新投遞該訊息;如果訊息再次消費失敗則入重試佇列 Q2,Q2 的重新投遞延遲為 10s,在 10s 過後再次投遞該訊息。以此類推,重試越多次重新投遞的時間就越久,為此需要設定一個上限,超過投遞次數就入死信佇列。重試佇列與延遲佇列有相同的地方,都是需要設定延遲級別,它們彼此的區別是:延遲佇列動作由內部觸發,重試佇列動作由外部消費端觸發;延遲佇列作用一次,而重試佇列的作用範圍會向後傳遞。

消費模式:

消費模式分為推(push)模式和拉(pull)模式。推模式是指由 Broker 主動推送訊息至消費端,實時性較好,不過需要一定的流制機制來確保服務端推送過來的訊息不會壓垮消費端。而拉模式是指消費端主動向 Broker 端請求拉取(一般是定時或者定量)訊息,實時性較推模式差,但是可以根據自身的處理能力而控制拉取的訊息量。

640?wx_fmt=png


訊息回溯:

一般訊息在消費完成之後就被處理了,之後再也不能消費到該條訊息。訊息回溯正好相反,是指訊息在消費完成之後,還能消費到之前被消費掉的訊息。對於訊息而言,經常面臨的問題是“訊息丟失”,至於是真正由於訊息中介軟體的缺陷丟失還是由於使用方的誤用而丟失一般很難追查,如果訊息中介軟體本身具備訊息回溯功能的話,可以通過回溯消費復現“丟失的”訊息進而查出問題的源頭之所在。訊息回溯的作用遠不止與此,比如還有索引恢復、本地快取重建,有些業務補償方案也可以採用回溯的方式來實現。

訊息堆積 + 持久化:

流量削峰是訊息中介軟體的一個非常重要的功能,而這個功能其實得益於其訊息堆積能力。從某種意義上來講,如果一個訊息中介軟體不具備訊息堆積的能力,那麼就不能把它看做是一個合格的訊息中介軟體。訊息堆積分記憶體式堆積和磁碟式堆積。RabbitMQ 是典型的記憶體式堆積,但這並非絕對,在某些條件觸發後會有換頁動作來將記憶體中的訊息換頁到磁碟(換頁動作會影響吞吐),或者直接使用惰性佇列來將訊息直接持久化至磁碟中。Kafka 是一種典型的磁碟式堆積,所有的訊息都儲存在磁碟中。一般來說,磁碟的容量會比記憶體的容量要大得多,對於磁碟式的堆積其堆積能力就是整個磁碟的大小。從另外一個角度講,訊息堆積也為訊息中介軟體提供了冗餘儲存的功能。援引 紐約時報的案例,其直接將 Kafka 用作儲存系統。

訊息追蹤:

對於分散式架構系統中的鏈路追蹤(trace)而言,大家一定不會陌生。對於訊息中介軟體而言,訊息的鏈路追蹤(以下簡稱訊息追蹤)同樣重要。對於訊息追蹤最通俗的理解就是要知道訊息從哪來,存在哪裡以及發往哪裡去。基於此功能下,我們可以對傳送或者消費完的訊息進行鏈路追蹤服務,進而可以進行問題的快速定位與排查。

訊息過濾:

訊息過濾是指按照既定的過濾規則為下游使用者提供指定類別的訊息。就以 kafka 而言,完全可以將不同類別的訊息傳送至不同的 topic 中,由此可以實現某種意義的訊息過濾,或者 Kafka 還可以根據分割槽對同一個 topic 中的訊息進行分類。不過更加嚴格意義上的訊息過濾應該是對既定的訊息採取一定的方式按照一定的過濾規則進行過濾。同樣以 Kafka 為例,可以通過客戶端提供的 ConsumerInterceptor 介面或者 Kafka Stream 的 filter 功能進行訊息過濾。

多租戶:

也可以稱為多重租賃技術,是一種軟體架構技術,主要用來實現多使用者的環境下公用相同的系統或程式元件,並且仍可以確保各使用者間資料的隔離性。RabbitMQ 就能夠支援多租戶技術,每一個租戶表示為一個 vhost,其本質上是一個獨立的小型 RabbitMQ 伺服器,又有自己獨立的佇列、交換器及繫結關係等,並且它擁有自己獨立的許可權。vhost 就像是物理機中的虛擬機器一樣,它們在各個例項間提供邏輯上的分離,為不同程式安全保密地允許資料,它既能將同一個 RabbitMQ 中的眾多客戶區分開,又可以避免佇列和交換器等命名衝突。

640?wx_fmt=png


流量控制:

流量控制(flow control)針對的是傳送方和接收方速度不匹配的問題,提供一種速度匹配服務抑制傳送速率使接收方應用程式的讀取速率與之相適應。通常的流控方法有 Stop-and-wait、滑動視窗以及令牌桶等。

訊息順序性:

顧名思義,訊息順序性是指保證訊息有序。這個功能有個很常見的應用場景就是 CDC(Change Data Chapture),以 MySQL 為例,如果其傳輸的 binlog 的順序出錯,比如原本是先對一條資料加 1,然後再乘以 2,傳送錯序之後就變成了先乘以 2 後加 1 了,造成了資料不一致。

640?wx_fmt=png


Kafka 自 0.11 版本開始引入了冪等性和事務,Kafka 的冪等性是指單個生產者對於單分割槽單會話的冪等,而事務可以保證原子性地寫入到多個分割槽,即寫入到多個分割槽的訊息要麼全部成功,要麼全部回滾,這兩個功能加起來可以讓 Kafka 具備 EOS(Exactly Once Semantic)的能力。

不過如果要考慮全域性的冪等,還需要與從上下游方面綜合考慮,即關聯業務層面,冪等處理本身也是業務層面所需要考慮的重要議題。以下游消費者層面為例,有可能消費者消費完一條訊息之後沒有來得及確認訊息就發生異常,等到恢復之後又得重新消費原來消費過的那條訊息,那麼這種型別的訊息冪等是無法有訊息中介軟體層面來保證的。如果要保證全域性的冪等,需要引入更多的外部資源來保證,比如以訂單號作為唯一性標識,並且在下游設定一個去重表。

事務性訊息:

事務本身是一個並不陌生的詞彙,事務是由事務開始(Begin Transaction)和事務結束(End Transaction)之間執行的全體操作組成。支援事務的訊息中介軟體並不在少數,Kafka 和 RabbitMQ 都支援,不過此兩者的事務是指生產者發生訊息的事務,要麼傳送成功,要麼傳送失敗。訊息中介軟體可以作為用來實現分散式事務的一種手段,但其本身並不提供全域性分散式事務的功能。

下表是對 Kafka 與 RabbitMQ 功能的總結性對比及補充說明:

640?wx_fmt=jpeg

640?wx_fmt=jpeg

640?wx_fmt=jpeg

6、具體技術選型指標2:效能


功能維度是訊息中介軟體選型中的一個重要的參考維度,但這並不是唯一的維度。有時候效能比功能還要重要,況且效能和功能很多時候是相悖的,魚和熊掌不可兼得,Kafka 在開啟冪等、事務功能的時候會使其效能降低,RabbitMQ 在開啟 rabbitmq_tracing 外掛的時候也會極大的影響其效能。訊息中介軟體的效能一般是指其吞吐量,雖然從功能維度上來說,RabbitMQ 的優勢要大於 Kafka,但是 Kafka 的吞吐量要比 RabbitMQ 高出 1 至 2 個數量級,一般 RabbitMQ 的單機 QPS 在萬級別之內,而 Kafka 的單機 QPS 可以維持在十萬級別,甚至可以達到百萬級。

訊息中介軟體的吞吐量始終會受到硬體層面的限制。就以網路卡頻寬為例,如果單機單網路卡的頻寬為 1Gbps,如果要達到百萬級的吞吐,那麼訊息體大小不得超過 (1Gb/8)/100W,即約等於 134B,換句話說如果訊息體大小超過 134B,那麼就不可能達到百萬級別的吞吐。這種計算方式同樣可以適用於記憶體和磁碟。


時延作為效能維度的一個重要指標,卻往往在訊息中介軟體領域所被忽視,因為一般使用訊息中介軟體的場景對時效性的要求並不是很高,如果要求時效性完全可以採用 RPC 的方式實現。訊息中介軟體具備訊息堆積的能力,訊息堆積越大也就意味著端到端的時延也就越長,與此同時延時佇列也是某些訊息中介軟體的一大特色。那麼為什麼還要關注訊息中介軟體的時延問題呢?訊息中介軟體能夠解耦系統,對於一個時延較低的訊息中介軟體而言,它可以讓上游生產者傳送訊息之後可以迅速的返回,也可以讓消費者更加快速的獲取到訊息,在沒有堆積的情況下可以讓整體上下游的應用之間的級聯動作更加高效,雖然不建議在時效性很高的場景下使用訊息中介軟體,但是如果所使用的訊息中介軟體的時延方面比較優秀,那麼對於整體系統的效能將會是一個不小的提升。

7、具體技術選型指標3:可靠性 + 可用性


訊息丟失是使用訊息中介軟體時所不得不面對的一個同點,其背後訊息可靠性也是衡量訊息中介軟體好壞的一個關鍵因素。尤其是在金融支付領域,訊息可靠性尤為重要。然而說到可靠性必然要說到可用性,注意這兩者之間的區別,訊息中介軟體的可靠性是指對訊息不丟失的保障程度;而訊息中介軟體的可用性是指無故障執行的時間百分比,通常用幾個 9 來衡量。

640?wx_fmt=png


這裡還要提及的一個方面是擴充套件能力,這裡我狹隘地將此歸納到可用性這一維度,訊息中介軟體的擴充套件能力能夠增強其用可用能力及範圍,比如前面提到的 RabbitMQ 支援多種訊息協議,這個就是基於其外掛化的擴充套件實現。還有從叢集部署上來講,歸功於 Kafka 的水平擴充套件能力,其基本上可以達到線性容量提升的水平,在 LinkedIn 實踐介紹中就提及了有部署超過千臺裝置的 Kafka 叢集。

8、具體技術選型指標4:運維管理


在訊息中介軟體的使用過程中難免會出現各式各樣的異常情況,有客戶端的,也有服務端的,那麼怎樣及時有效的進行監測及修復。業務線流量有峰值又低谷,尤其是電商領域,那麼怎樣前進行有效的容量評估,尤其是大促期間?腳踢電源、網線被挖等事件層出不窮,如何有效的做好異地多活?這些都離不開訊息中介軟體的衍生產品——運維管理。

運維管理也可以進行進一步的細分,比如:申請、稽核、監控、告警、管理、容災、部署等。

申請、稽核很好理解,在源頭對資源進行管控,既可以進行有效校正應用方的使用規範,配和監控也可以做好流量統計與流量評估工作,一般申請、稽核與公司內部系統交融性較大,不適合使用開源類的產品。

監控、告警也比較好理解,對訊息中介軟體的使用進行全方位的監控,即可以為系統提供基準資料,也可以在檢測到異常的情況配合告警,以便運維、開發人員的迅速介入。除了一般的監控項(比如硬體、GC 等)之外,對於訊息中介軟體還需要關注端到端時延、訊息審計、訊息堆積等方面。對於 RabbitMQ 而言,最正統的監控管理工具莫過於 rabbitmq_management 外掛了,但是社群內還有 AppDynamics, Collectd, DataDog, Ganglia, Munin, Nagios, New Relic, Prometheus, Zenoss 等多種優秀的產品。Kafka 在此方面也毫不遜色,比如:Kafka Manager, Kafka Monitor, Kafka Offset Monitor, Burrow, Chaperone, Confluent Control Center 等產品,尤其是 Cruise 還可以提供自動化運維的功能。

不管是擴容、降級、版本升級、叢集節點部署、還是故障處理都離不開管理工具的應用,一個配套完備的管理工具集可以在遇到變更時做到事半功倍。故障可大可小,一般是一些應用異常,也可以是機器掉電、網路異常、磁碟損壞等單機故障,這些故障單機房內的多副本足以應付。如果是機房故障就要涉及異地容災了,關鍵點在於如何有效的進行資料複製,對於 Kafka 而言,可以參考 MirrorMarker、uReplicator 等產品,而 RabbitMQ 可以參考 Federation 和 Shovel。

9、具體技術選型指標5:社群力度及生態發展


對於目前流行的程式語言而言,如 Java、Python,如果你在使用過程中遇到了一些異常,基本上可以通過搜尋引擎的幫助來得到解決,因為一個產品用的人越多,踩過的坑也就越多,對應的解決方案也就越多。對於訊息中介軟體也同樣適用,如果你選擇了一種“生僻”的訊息中介軟體,可能在某些方面運用的得心應手,但是版本更新緩慢、遇到棘手問題也難以得到社群的支援而越陷越深;相反如果你選擇了一種“流行”的訊息中介軟體,其更新力度大,不僅可以迅速的彌補之前的不足,而且也能順應技術的快速發展來變更一些新的功能,這樣可以讓你以“站在巨人的肩膀上”。在運維管理維度我們提及了 Kafka 和 RabbitMQ 都有一系列開源的監控管理產品,這些正是得益於其社群及生態的迅猛發展。

10、訊息中介軟體選型誤區總結


在進行訊息中介軟體選型之前可以先問自己一個問題:是否真的需要一個訊息中介軟體?在搞清楚這個問題之後,還可以繼續問自己一個問題:是否需要自己維護一套訊息中介軟體?很多初創型公司為了節省成本會選擇直接購買訊息中介軟體有關的雲服務,自己只需要關注收發訊息即可,其餘的都可以外包出去。

很多人面對訊息中介軟體時會有一種自研的衝動,你完全可以對 Java 中的 ArrayBlockingQueue 做一個簡單的封裝,你也可以基於檔案、資料庫、Redis 等底層儲存封裝而形成一個訊息中介軟體。訊息中介軟體做為一個基礎元件並沒有想象中的那麼簡單,其背後還需要配套的管理運維整個生態的產品集。自研還有會交接問題,如果文件不齊全、運作不規範將會帶給新人噩夢般的體驗。是否真的有自研的必要?如果不是 KPI 的壓迫可以先考慮下這 2 個問題:1. 目前市面上的訊息中介軟體是否都真的無法滿足目前業務需求? 2. 團隊是否有足夠的能力、人力、財力、精力來支援自研?

很多人在做訊息中介軟體選型時會參考網路上的很多對比類的文章,但是其專業性、嚴謹性、以及其政治立場問題都有待考證,需要帶著懷疑的態度去審視這些文章。比如有些文章會在沒有任何限定條件及場景的情況下直接定義某款訊息中介軟體最好,還有些文章沒有指明訊息中介軟體版本及測試環境就來做功能和效能對比分析,諸如此類的文章都可以唾棄之。

訊息中介軟體猶如小馬過河,選擇合適的才最重要,這需要貼合自身的業務需求,技術服務於業務,大體上可以根據上一節所提及的功能、效能等 6 個維度來一一進行篩選。更深層次的抉擇在於你能否掌握其魂,筆者鄙見:RabbitMQ 在於 routing,而 Kafka 在於 streaming,瞭解其根本對於自己能夠對症下藥選擇到合適的訊息中介軟體尤為重要。

訊息中介軟體選型切忌一味的追求效能或者功能,效能可以優化,功能可以二次開發。如果要在功能和效能方面做一個抉擇的話,那麼首選效能,因為總體上來說效能優化的空間沒有功能擴充套件的空間大。然而對於長期發展而言,生態又比效能以及功能都要重要。

很多時候,對於可靠性方面也容易存在一個誤區:想要找到一個產品來保證訊息的絕對可靠,很不幸的是這世界上沒有絕對的東西,只能說盡量趨於完美。想要儘可能的保障訊息的可靠性也並非單單隻靠訊息中介軟體本身,還要依賴於上下游,需要從生產端、服務端和消費端這 3 個維度去努力保證,《RabbitMQ 訊息可靠性分析》這篇文章就從這 3 個維度去分析了 RabbitMQ 的可靠性。

訊息中介軟體選型還有一個考量標準就是儘量貼合團隊自身的技術棧體系,雖然說沒有蹩腳的訊息中介軟體只有蹩腳的程式設計師,但是讓一個 C 棧的團隊去深挖 PhxQueue 總比去深挖 Scala 編寫的 Kafka 要容易的多。

訊息中介軟體大道至簡:一發一存一消費,沒有最好的訊息中介軟體,只有最合適的訊息中介軟體

公眾號推薦:

640?wx_fmt=jpeg

640?wx_fmt=jpeg

640?wx_fmt=jpeg

相關文章