天天看點

深入了解MQ:消息中間件的技術選型

作者:JAVA後端架構
深入了解MQ:消息中間件的技術選型

1 背景

在高并發、高消息吞吐的網際網路場景中,我們經常會使用消息隊列(Message Queue)作為基礎設施,在服務端架構中擔當消息中轉、消息削峰、事務異步處理 等職能。

對于那些不需要實時響應的的業務,我們都可以放在消息隊列中進行傳輸。下面是使用者在進行系統注冊的時候場景,充分展現MQ的作用

深入了解MQ:消息中間件的技術選型

可以看到使用者注冊的過程步驟1+步驟2,從請求到響應總共耗時 55 ms。消息消費+短信發送的時間比較長,從上面看花了5s多,一般讓消息隊列服務去處理,使用者靜靜等待短信送達即可。

消息隊列中間件(簡稱消息中間件)是指利用高效可靠的消息傳遞機制進行與平台無關的資料交流,并基于資料通信來進行分布式系統的內建。通過提供消息傳遞和消息排隊模型,它可以在分布式環境下提供應用解耦、

彈性伸縮、備援存儲、流量削峰、異步通信、資料同步等等功能,其作為分布式系統架構中的一個重要元件,有着舉足輕重的地位。

2 消息中間件的組成

Broker:消息伺服器,以服務的形式運作在server端,給各個業務系統提供核心消息資料的中轉服務。

Producer:消息生産者,業務的發起方,負責生産消息傳輸給broker。

Consumer:消息消費者,業務的處理方,負責從broker擷取消息并進行業務邏輯處理

Topic:主題子產品,釋出/訂閱模式下的消息統一彙集地,不同生産者向topic發送消息,由MQ伺服器分發到不同的訂閱者,實作消息的廣播

Queue:隊列,PTP模式下,特定生産者向特定queue發送消息,消費者訂閱特定的queue完成指定消息的接收。

Message:消息體,根據不同通信協定定義的固定格式進行編碼的資料包,來封裝業務資料,實作消息的傳輸。

深入了解MQ:消息中間件的技術選型

這邊以kafka為例子,這是典型的叢集模式,Kafka通過Zookeeper管理叢集配置,選舉leader,以及在Consumer Group發生變化時進行rebalance。Producer使用push模式将消息釋出到broker,Consumer使用pull模式從broker訂閱并消費消息。

  • producer 負責生産消息
  • consumer 負責消費消息
  • broker 消息伺服器,提供消息核心的處理工作
  • zookeeper 用于生産者和消費者的注冊與發現

3 消息中間件的模式分類

PTP點對點:使用queue作為通信載體

消息生産者生産消息發送到queue中,然後消息消費者從queue中取出并且消費消息。

不可重複消費,消息被消費以後,queue中不再存儲,是以消息消費者不可能消費到已經被消費的消息。 Queue支援存在多個消費者,但是對一個消息而言,隻會有一個消費者可以消費。

Pub/Sub釋出訂閱(廣播):使用topic作為通信載體

消息生産者(釋出)将消息釋出到topic中,同時有多個消息消費者(訂閱)消費該消息。和點對點方式不同,釋出到topic的消息會被所有訂閱者消費,是以從1到N個訂閱者都能得到這個消息的拷貝。

4 消息中間件的優勢

系統解耦:互動系統之間沒有直接的調用關系,隻是通過消息傳輸,故系統侵入性不強,耦合度低。

削峰、提高系統響應時間:例如原來的一套邏輯,可将緊急重要(需要立刻響應)的業務放到該調用方法中,響應要求不高的使用消息隊列,放到MQ隊列中,供消費者處理。

業務的有序性處理:先來先處理,比如一個系統處理某件事需要很長一段時間,但是在處理這件事情時候,有其他人也發出了請求,可以把請求放在消息隊裡,一個一個來處理

為大資料處理架構提供服務:通過消息作為整合,大資料的背景下,消息隊列還與實時處理架構整合,為資料處理提供性能支援。

5 消息中間件常用協定

AMQP協定、MQTT協定、STOMP協定、XMPP協定、其他基于TCP/IP自定義的協定。

6 豐富強大的消息中間件生态

目前開源的消息中間還是很豐富的,大家用的比較多的比如 ActiveMQ、RabbitMQ、Kafka、RocketMQ、ZeroMQ 等。

但是每個人的業務場景不一樣,受限于系統的規模,業務的取舍(如延遲容忍度,死信、重試的需求,可持久化需求),并不是每一款消息中間件都能滿足你的需求。

除了個别大廠會進行自研(如 阿裡的Rocket MQ、滴滴的DD MQ)之外,大部分同學還是要對選型有一些思考的。各自都有各自的側重點,選擇合适自己、揚長避短無疑是最好的方式。

6.1 主流MQ介紹

下面基于閱聽人程度,對三款主流的MQ做介紹,通過各項名額上的對比,給出我們在實際應用場景中的建議。

RabbitMQ:

采用 Erlang 語言實作的 AMQP 協定的消息中間件,起源于金融系統,廣泛應用在分布式系統中,承擔消息轉發的職責。RabbitMQ 發展曆史比較久遠,影響範圍比較大,被很多開發者認可,在可靠性、可用性、可擴充性、功能性方面有着非凡表現。

RocketMQ:

阿裡開源的消息中間件,目前已經捐獻給 Apache 基金會,它是由 Java 語言開發的,具備高吞吐量、高可用性、适合大規模分布式系統應用等特點。并且在阿裡的雙11、618等重要活動中經受住了考驗。

Kafka:

起初是由 LinkedIn 公司采用 Scala 語言開發的一個分布式、多分區、多副本且基于 zookeeper 協調的分布式消息系統,現已捐獻給 Apache 基金會。它是一種高吞吐量的分布式釋出訂閱消息系統,以可水準擴充和高吞吐率而被廣泛使用。

目前越來越多的開源分布式處理系統如 Cloudera、Apache Storm、Spark、Flink 等都支援與 Kafka 內建。

6.2 主流MQ對比

深入了解MQ:消息中間件的技術選型

6.3 選型建議

6.3.1 系統建設規模角度

中小型系統建議選用RabbitMQ,資料量相對較小,選型應首選功能比較完備的,是以kafka排除。RocketMQ是阿裡出品,如果阿裡放棄維護,中小型公司一般很難投入人力進行RocketMQ的定制化開發,是以不推薦。

6.3.2 業務規模角度

根據具體使用規模在RocketMQ和kafka之間二選一。

大型業務系統:有實際的業務體量需求,比如足夠大規模的分布式環境,以及足夠大的資料量。這時候 RocketMQ 和 kafka 都是10w+的吞吐量,都可以在考慮範圍内。

如果你有業務定制需求,可以優先選用RocketMQ,畢竟是開源的,大的業務系統也願意花精力去優化JAVA源碼的。至于kafka,根據業務方向選擇,類似日志采集功能,首選kafka,因為他在日志上報、監控資料采集方面有着大規模的實踐經驗,這也是他們主打的應用場景。

具體該選哪個,看使用場景。引入MQ之後,也會有一定的弊端,必然一定程度上降低系統可用性,增加複雜性。

6.3.3 功能性層面選型

深入了解MQ:消息中間件的技術選型
  • 優先級隊列:可配置優先級,優先級高的消息具備優先被消費的特權,這樣可以為下遊服務提供不同消息級别的保證。這種模式隻是在生産效率高于消費效率的時候才有效果。如果消費者的消費速度大于生産者的速度,消息中間件伺服器(Broker)中沒有消息堆積,就不存在對待消費資料進行優先級排序的需求了。
  • 延遲隊列:延遲隊列會存儲對應的延遲消息,延遲消息是指消息被生産後,并不馬上消費,而是等待一定時間後,消費者才拿到消息進行消費。延遲隊列的模式分為兩種,基于消息的延遲和基于隊列的延遲。基于消息的延遲是指為每條消息設定不同的延遲時間,那麼每當隊列中有新消息進入的時候就會重新根據延遲時間排序,但是這會對性能造成很大的影響。基于隊列的延遲,設定不同延遲級别的隊列,如 15s、30s、1m、10m 等,每個隊列中消息的延遲時間都是相同的,這樣不需要消耗大量性能去做延遲時間排序,每個消息都有固定的投遞時間。
  • 延遲隊列的常用的場景有以下幾種:
  • 1、購買火車票提示:30分鐘之内未付款,将自動取消訂單!
  • 2、雙11網購時,距離聚劃算活動開始時間還有 17小時,到時全場5折優惠。
  • 死信隊列:由于某些原因消息無法被正确的投遞,為了確定消息不會被無故的丢棄,一般會存儲到一個特殊的隊列中,我們稱之為死信隊列。與此對應的還有一個“回退隊列”的概念,試想如果消費者在消費時發生了異常,那麼就不會對這一次消費進行确認(Ack), 進而發生復原消息的操作之後消息始終會放在隊列的頂部,然後不斷被處理和復原,導緻隊列陷入死循環。為了解決這個問題,可以為每個隊列設定一個回退隊列,它和死信隊列都是為異常的處理提供的一種機制保障。實際情況下,回退隊列的角色可以由死信隊列和重試隊列來扮演。
  • 重試隊列:重試隊列其實可以看成是一種回退隊列,具體指消費端消費消息失敗時,為防止消息無故丢失而重新将消息復原到 Broker 中。與回退隊列不同的是重試隊列一般分成多個重試等級,每個重試等級一般也會設定重新投遞延時,重試次數越多投遞延時就越大。比如第一次重試延遲時間為5s,再次消費失敗後延遲重試時間為10s,以此類推,重試越多次重新投遞的時間就越久。為了避免延遲時間被無限放大,需要有個重試次數限制,超過就寫入死信隊列。這邊需要注意:延遲隊列動作由内部觸發,重試隊列動作由外部消費端觸發。
  • 消費模式:消費模式分為推(push)模式和拉(pull)模式。推模式是指由 Broker 主動推送消息至消費端,實時性較好,不過需要保證服務端推送的消息不會嚴重超過消費端消化能力。而拉模式是指消費端定時定量主動向 Broker 端請求拉取消息,雖然實時性較差,但是可以根據自身的消費能力來拉取。
  • 廣播消費:消息一般有兩種發送模式:點對點(P2P,Point-to-Point)模式和釋出/訂閱(Pub/Sub)模式。對于P2P模式而言,消息被消費以後,隊列中不會再存儲,即使有多個消費者,一條消息隻會被一個消費者消費。而釋出訂閱(Pub/Sub)模式定義了如何向一個内容節點釋出和訂閱消息,這個内容節點稱為主題(topic),主題可以認為是消息傳遞的中介,消息釋出者将消息釋出到某個主題,而消息訂閱者則從主題中訂閱消息。主題使得消息的訂閱者與消息的釋出者互相保持獨立,不需要進行接觸即可保證消息的傳遞,釋出 / 訂閱模式在消息的一對多廣播時采用。RabbitMQ 是一種典型的點對點模式,而 Kafka 是一種典型的釋出訂閱模式。
  • 消息回溯:一般消息在消費完成之後就被處理了,之後再也不能消費到該條消息。消息回溯正好相反,是指消息在消費完成之後,還能追溯到之前被消費掉的消息。
  • 消息堆積 + 持久化:進行流量的削峰填谷是消息中間件的一個核心功能,實作的能力主要展現在消息堆積能力上。消息堆積分記憶體式堆積和磁盤式堆積。RabbitMQ 是典型的記憶體式堆積,可以通過一些方式持久化到磁盤中,但是會降低一些性能。Kafka 是典型的磁盤式堆積,所有的消息都存儲在磁盤中,存儲容量是有了很大的提升,但是磁盤性能會比記憶體差很多。
  • 消息追蹤:在消息中間件中,消息的鍊路追蹤非常重要,它可以對生産和消費過的消息進行trace追蹤。這樣,在出現故障的時候,就可以快速的定位問題。
  • 消息過濾:消息過濾是指按照既定的過濾規則為下遊使用者提供指定類别的消息。就以 kafka 而言,完全可以将不同類别的消息發送至不同的 topic 中,由此可以實作某種意義的消息過濾,或者 Kafka 還可以根據分區對同一個 topic 中的消息進行分類。不過更加嚴格意義上的消息過濾應該是對既定的消息采取一定的方式按照一定的過濾規則進行過濾。同樣以 Kafka 為例,可以通過用戶端提供的 ConsumerInterceptor 接口或者 Kafka Stream 的 filter 功能進行消息過濾。
  • 流量控制:flow control,當生産者和消費者 處理速度不均衡問題,通過對生産者和消費者的限流,來保障兩者的均衡。通常的流控方法有 Stop-and-wait、滑動視窗以及令牌桶等。
  • 消息順序性:順序性是指保證消息有序,特别是分布式場景下,有序的執行,是保證一緻性 (Consistency)的前提。
  • 消息幂等性:對于確定消息在生産者和消費者之間進行傳輸而言一般有三種傳輸保障(delivery guarantee):At most once,至多一次,消息可能丢失,但絕不會重複傳輸;At least once,至少一次,消息絕不會丢,但是可能會重複;Exactly once,精确一次,每條消息肯定會被傳輸一次且僅一次。對于大多數消息中間件而言,一般隻提供 At most once 和 At least once 兩種傳輸保障,對于第三種一般很難做到,由此消息幂等性也很難保證。
  • 事務性消息:原子性事務中的操作為一個整體,要麼都做,要麼都不做。即一旦出錯,就復原事務,事務是由事務開始(Begin Transaction)和事務結束(End Transaction)之間執行的全體操作組成。Kafka 和 RabbitMQ 都支援,不過僅僅指的是生産者發送消息是一個事務性操作,要麼發送成功,要麼發送失敗。

6.3.4 性能層面

功能次元是消息中間件選型中的一個重要的參考次元,但性能也是考慮的一個重要環節。

吞吐量角度:Kafka 在開啟幂等、事務功能的時候會使其性能降低,RabbitMQ 在開啟 rabbitmq_tracing 插件的時候也會極大的影響其性能。消息中間件的性能一般是指其吞吐量,雖然從功能次元上來說,RabbitMQ 的優勢要大于 Kafka,但是 Kafka 的吞吐量要比 RabbitMQ 高出 1 至 2 個數量級,一般 RabbitMQ 的單機 QPS 在萬級别之内,而 Kafka 的單機 QPS 可以維持在十萬級别,甚至可以達到百萬級。

時延角度:另外一個是時延,作為性能次元的一個重要名額,卻往往在消息中間件領域所被忽視,因為一般使用消息中間件的場景對時效性的要求并不是很高,如果要求時效性完全可以采用 RPC 的方式實作。消息中間件具備消息堆積的能力。Kafka是ms以内,RabbitMQ是us級别的。

6.3.5 高可用角度

高可用角度是指系統的出錯機率和無故障運作時長。

如消息丢失,是使用消息中間件時所不得不面對的一個同點,其背後消息可靠性也是衡量消息中間件好壞的一個關鍵因素。尤其是在金融支付領域,消息可靠性尤為重要。然而說到可靠性必然要說到可用性,注意這兩者之間的差別,消息中間件的可靠性是指對消息不丢失的保障程度;

而消息中間件的可用性是指無故障運作的時間百分比,通常用幾個 9 來衡量,如 99.99% 就是一個不錯的名額。

對應的 RabbitMQ 是通過鏡像環形隊列實作多副本及強一緻性語義的。多副本可以保證在 master 節點當機異常之後可以提升 slave 作為新的 master 而繼續提供服務來保障可用性。

6.3.6 運維管理層面

消息中間件一個很重要的考慮層面是運維管理,比如:申請、稽核、監控、告警、管理、容災、部署等。

對消息中間件的使用 從使用、接入規範、全方位的監控、流量統計和分析等方面,提供有效的基準資料,也可以在檢測到異常的情況配合告警,以便運維、開發人員的迅速介入。除了一般的監控項(比如硬體、GC 等)之外,對于消息中間件還需要關注端到端時延、消息審計、消息堆積等方面。

對于 RabbitMQ 而言,最正統的監控管理工具莫過于 rabbitmq_management 插件了,另外還有 AppDynamics, Collectd, DataDog, Ganglia 等多種優秀的産品。

Kafka 豐富的管理工具,比如:Kafka Manager, Kafka Monitor, Kafka Offset Monitor 等産品,其中 Cruise 還可以提供自動化運維的功能。

6.3.7 社群力度及生态發展

Kafka 和 RabbitMQ 都有一系列開源的監控管理産品,社群活躍,産品生态都很不錯。

為幫助開發者們提升面試技能、有機會入職BATJ等大廠公司,特别制作了這個專輯——這一次整體放出。

大緻内容包括了: Java 集合、JVM、多線程、并發程式設計、設計模式、Spring全家桶、Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、MongoDB、Redis、MySQL、RabbitMQ、Kafka、Linux、Netty、Tomcat等大廠面試題等、等技術棧!

深入了解MQ:消息中間件的技術選型

歡迎大家關注公衆号【Java爛豬皮】,回複【666】,擷取以上最新Java後端架構VIP學習資料以及視訊學習教程,然後一起學習,一文在手,面試我有。

每一個專欄都是大家非常關心,和非常有價值的話題,如果我的文章對你有所幫助,還請幫忙點贊、好評、轉發一下,你的支援會激勵我輸出更高品質的文章,非常感謝!

深入了解MQ:消息中間件的技術選型