前言
訊息佇列 已經逐漸成為企業應用系統 內部通訊 的核心手段。它具有 低耦合、可靠投遞、廣播、流量控制、最終一致性 等一系列功能。
當前使用較多的 訊息佇列 有 RabbitMQ
、RocketMQ
、ActiveMQ
、Kafka
、ZeroMQ
、MetaMQ
等,而部分 資料庫 如 Redis
、MySQL
以及 phxsql
也可實現訊息佇列的功能。
正文
1. 訊息佇列概述
訊息佇列 是指利用 高效可靠 的 訊息傳遞機制 進行與平臺無關的 資料交流,並基於 資料通訊 來進行分散式系統的整合。
通過提供 訊息傳遞 和 訊息排隊 模型,它可以在 分散式環境 下提供 應用解耦、彈性伸縮、冗餘儲存、流量削峰、非同步通訊、資料同步 等等功能,其作為 分散式系統架構 中的一個重要元件,有著舉足輕重的地位。
2. 訊息佇列的特點
2.1. 採用非同步處理模式
訊息傳送者 可以傳送一個訊息而無須等待響應。訊息傳送者 將訊息傳送到一條 虛擬的通道(主題 或 佇列)上,訊息接收者 則 訂閱 或是 監聽 該通道。一條資訊可能最終轉發給 一個或多個 訊息接收者,這些接收者都無需對 訊息傳送者 做出 同步回應。整個過程都是 非同步的。
2.2. 應用系統之間解耦合
主要體現在如下兩點:
-
傳送者和接受者不必瞭解對方、只需要 確認訊息;
-
傳送者和接受者 不必同時線上。
比如線上交易系統為了保證資料的 最終一致,在 支付系統 處理完成後會把 支付結果 放到 訊息中介軟體 裡,通知 訂單系統 修改 訂單支付狀態。兩個系統是通過訊息中介軟體解耦的。
3. 訊息佇列的傳遞服務模型
訊息佇列的 傳遞服務模型 如下圖所示:
4. 訊息佇列的的傳輸模式
4.1. 點對點模型
點對點模型 用於 訊息生產者 和 訊息消費者 之間 點到點 的通訊。訊息生產者將訊息傳送到由某個名字標識的特定消費者。這個名字實際上對於消費服務中的一個 佇列(Queue
),在訊息傳遞給消費者之前它被 儲存 在這個佇列中。佇列訊息 可以放在 記憶體 中也可以 持久化,以保證在訊息服務出現故障時仍然能夠傳遞訊息。
傳統的點對點訊息中介軟體通常由 訊息佇列服務、訊息傳遞服務、訊息佇列 和 訊息應用程式介面 API
組成,其典型的結構如下圖所示。
特點:
- 每個訊息只用一個消費者;
- 傳送者和接受者沒有時間依賴;
- 接受者確認訊息接受和處理成功。
示意圖如下所示:
4.2. 釋出/訂閱模型(Pub/Sub)
釋出者/訂閱者 模型支援向一個特定的 訊息主題 生產訊息。0
或 多個訂閱者 可能對接收來自 特定訊息主題 的訊息感興趣。
在這種模型下,釋出者和訂閱者彼此不知道對方,就好比是匿名公告板。這種模式被概況為:多個消費者可以獲得訊息,在 釋出者 和 訂閱者 之間存在 時間依賴性。釋出者需要建立一個 訂閱(subscription
),以便能夠消費者訂閱。訂閱者 必須保持 持續的活動狀態 並 接收訊息。
在這種情況下,在訂閱者 未連線時,釋出的訊息將在訂閱者 重新連線 時 重新發布,如下圖所示:
特性:
- 每個訊息可以有多個訂閱者;
- 客戶端只有訂閱後才能接收到訊息;
- 持久訂閱和非持久訂閱。
注意:
- 釋出者和訂閱者有時間依賴:接受者和釋出者只有建立訂閱關係才能收到訊息;
- 持久訂閱:訂閱關係建立後,訊息就不會消失,不管訂閱者是否都線上;
- 非持久訂閱:訂閱者為了接受訊息,必須一直線上。 當只有一個訂閱者時約等於點對點模式
5. 訊息佇列應用場景
當你需要使用 訊息佇列 時,首先需要考慮它的必要性。可以使用訊息佇列的場景有很多,最常用的幾種,是做 應用程式鬆耦合、非同步處理模式、釋出與訂閱、最終一致性、錯峰流控 和 日誌緩衝 等。反之,如果需要 強一致性,關注業務邏輯的處理結果,則使用 RPC
顯得更為合適。
5.1. 非同步處理
非核心 流程 非同步化,減少系統 響應時間,提高 吞吐量。例如:簡訊通知、終端狀態推送、App
推送、使用者註冊 等。
訊息佇列 一般都內建了 高效的通訊機制,因此也可以用於單純的訊息通訊,比如實現 點對點訊息佇列 或者 聊天室 等。
應用案例
網站使用者註冊,註冊成功後會過一會傳送郵件確認或者短息。
5.2. 系統解耦
-
系統之間不是 強耦合的,訊息接受者 可以隨意增加,而不需要修改 訊息傳送者的程式碼。訊息傳送者 的成功不依賴 訊息接受者(比如:有些銀行介面不穩定,但呼叫方並不需要依賴這些介面)。
-
不強依賴 於非本系統的核心流程,對於 非核心流程,可以放到訊息佇列中讓 訊息消費者 去按需消費,而 不影響核心主流程。
5.3. 最終一致性
最終一致性 不是 訊息佇列 的必備特性,但確實可以依靠 訊息佇列 來做 最終一致性 的事情。
-
先寫訊息再操作,確保操作完成後再修改訊息狀態。定時任務補償機制 實現訊息 可靠傳送接收、業務操作的可靠執行,要注意 訊息重複 與 冪等設計。
-
所有不保證
100%
不丟訊息 的訊息佇列,理論上無法實現 最終一致性。
像
Kafka
一類的設計,在設計層面上就有 丟訊息 的可能(比如 定時刷盤,如果掉電就會丟訊息)。哪怕只丟千分之一的訊息,業務也必須用其他的手段來保證結果正確。
5.4. 廣播
生產者/消費者 模式,只需要關心訊息是否 送達佇列,至於誰希望訂閱和需要消費,是 下游 的事情,無疑極大地減少了開發和聯調的工作量。
5.5. 流量削峰和流控
當 上下游系統 處理能力存在差距的時候,利用 訊息佇列 做一個通用的 “漏斗”,進行 限流控制。在下游有能力處理的時候,再進行分發。
舉個例子:使用者在支付系統成功結賬後,訂單系統會通過簡訊系統向使用者推送扣費通知。 簡訊系統 可能由於 短板效應,速度卡在 閘道器 上(每秒幾百次請求),跟 前端的併發量 不是一個數量級。 於是,就造成 支付系統 和 簡訊系統 的處理能力出現差異化。
然而使用者晚上個半分鐘左右收到簡訊,一般是不會有太大問題的。如果沒有訊息佇列,兩個系統之間通過 協商、滑動視窗 等複雜的方案也不是說不能實現。但 系統複雜性 指數級增長,勢必在 上游 或者 下游 做 儲存,並且要處理 定時、擁塞 等一系列問題。而且每當有 處理能力有差距 的時候,都需要 單獨 開發一套邏輯來維護這套邏輯。
所以,利用中間系統轉儲兩個系統的通訊內容,並在下游系統有能力處理這些訊息的時候,再處理這些訊息,是一套相對較通用的方式。
應用案例
- 把訊息佇列當成可靠的 訊息暫存地,進行一定程度的 訊息堆積;
- 定時進行訊息投遞,比如模擬 使用者秒殺 訪問,進行 系統效能壓測。
5.6. 日誌處理
將訊息佇列用在 日誌處理 中,比如 Kafka
的應用,解決 海量日誌 傳輸和緩衝的問題。
應用案例
把日誌進行集中收集,用於計算 PV
、使用者行為分析 等等。
5.7. 訊息通訊
訊息佇列一般都內建了 高效的通訊機制,因此也可以用於單純的 訊息通訊,比如實現 點對點訊息佇列 或者 聊天室 等。
6. 訊息佇列的推拉模型
6.1. Push推訊息模型
訊息生產者 將訊息傳送給 訊息佇列,訊息佇列 又將訊息推給 訊息消費者。
6.2. Pull拉訊息模型
消費者 請求 訊息佇列 接受訊息,訊息生產者 從 訊息佇列 中拉該訊息。
6.3. 兩種型別的區別
7. 訊息佇列技術對比
本部分主要介紹四種常用的訊息佇列(ActiveMQ
/ RabbitMQ
/ RocketMQ
/ Kafka
)的主要特性、優點、缺點。
7.1. ActiveMQ
ActiveMQ
是由 Apache
出品,ActiveMQ
是一個完全支援JMS1.1
和 J2EE 1.4
規範的 JMS Provider
實現。它非常快速,支援 多種語言的客戶端 和 協議,而且可以非常容易的嵌入到企業的應用環境中,並有許多高階功能。
(a) 主要特性
-
服從JMS規範:
JMS
規範提供了良好的標準和保證,包括:同步 或 非同步 的訊息分發,一次和僅一次的訊息分發,訊息接收 和 訂閱 等等。遵從JMS
規範的好處在於,不論使用什麼JMS
實現提供者,這些基礎特性都是可用的; -
連線靈活性:
ActiveMQ
提供了廣泛的 連線協議,支援的協議有:HTTP/S
,IP
多播,SSL
,TCP
,UDP
等等。對眾多協議的支援讓ActiveMQ
擁有了很好的靈活性; -
支援的協議種類多:
OpenWire
、STOMP
、REST
、XMPP
、AMQP
; -
持久化外掛和安全外掛:
ActiveMQ
提供了 多種持久化 選擇。而且,ActiveMQ
的安全性也可以完全依據使用者需求進行 自定義鑑權 和 授權; -
支援的客戶端語言種類多:除了
Java
之外,還有:C/C++
,.NET
,Perl
,PHP
,Python
,Ruby
; -
代理叢集:多個
ActiveMQ
代理 可以組成一個 叢集 來提供服務; -
異常簡單的管理:
ActiveMQ
是以開發者思維被設計的。所以,它並不需要專門的管理員,因為它提供了簡單又使用的管理特性。有很多中方法可以 監控ActiveMQ
不同層面的資料,包括使用在JConsole
或者在ActiveMQ
的Web Console
中使用JMX
。通過處理JMX
的告警訊息,通過使用 命令列指令碼,甚至可以通過監控各種型別的 日誌。
(b) 部署環境
ActiveMQ
可以執行在 Java
語言所支援的平臺之上。使用 ActiveMQ
需要:
Java JDK
ActiveMQ
安裝包
(c) 優點
-
跨平臺 (
JAVA
編寫與平臺無關,ActiveMQ
幾乎可以執行在任何的JVM
上); -
可以用
JDBC
:可以將 資料持久化 到資料庫。雖然使用JDBC
會降低ActiveMQ
的效能,但是資料庫一直都是開發人員最熟悉的儲存介質; -
支援
JMS
規範:支援JMS
規範提供的 統一介面; -
支援 自動重連 和 錯誤重試機制;
-
有安全機制:支援基於
shiro
,jaas
等多種 安全配置機制,可以對Queue/Topic
進行 認證和授權; -
監控完善:擁有完善的 監控,包括
Web Console
,JMX
,Shell
命令列,Jolokia
的RESTful API
; -
介面友善:提供的
Web Console
可以滿足大部分情況,還有很多 第三方的元件 可以使用,比如hawtio
;
(d) 缺點
-
社群活躍度不及
RabbitMQ
高; -
根據其他使用者反饋,會出莫名其妙的問題,會 丟失訊息;
-
目前重心放到
activemq 6.0
產品Apollo
,對5.x
的維護較少; -
不適合用於 上千個佇列 的應用場景;
7.2. RabbitMQ
RabbitMQ
於 2007
年釋出,是一個在 AMQP
(高階訊息佇列協議)基礎上完成的,可複用的企業訊息系統,是當前最主流的訊息中介軟體之一。
(a) 主要特性
-
可靠性:提供了多種技術可以讓你在 效能 和 可靠性 之間進行 權衡。這些技術包括 永續性機制、投遞確認、釋出者證實 和 高可用性機制;
-
靈活的路由:訊息在到達佇列前是通過 交換機 進行 路由 的。
RabbitMQ
為典型的路由邏輯提供了 多種內建交換機 型別。如果你有更復雜的路由需求,可以將這些交換機組合起來使用,你甚至可以實現自己的交換機型別,並且當做RabbitMQ
的 外掛 來使用; -
訊息叢集:在相同區域網中的多個
RabbitMQ
伺服器可以 聚合 在一起,作為一個獨立的邏輯代理來使用; -
佇列高可用:佇列可以在叢集中的機器上 進行映象,以確保在硬體問題下還保證 訊息安全;
-
支援多種協議:支援 多種訊息佇列協議;
-
支援多種語言:用
Erlang
語言編寫,支援只要是你能想到的 所有程式語言; -
管理介面:
RabbitMQ
有一個易用的 使用者介面,使得使用者可以 監控 和 管理 訊息Broker
的許多方面; -
跟蹤機制:如果 訊息異常,
RabbitMQ
提供訊息跟蹤機制,使用者可以找出發生了什麼; -
外掛機制:提供了許多 外掛,來從多方面進行擴充套件,也可以編寫自己的外掛。
(b) 部署環境
RabbitMQ
可以執行在 Erlang
語言所支援的平臺之上,包括 Solaris
,BSD
,Linux
,MacOSX
,TRU64
,Windows
等。使用 RabbitMQ
需要:
ErLang
語言包RabbitMQ
安裝包
(c) 優點
-
由於
Erlang
語言的特性,訊息佇列效能較好,支援 高併發; -
健壯、穩定、易用、跨平臺、支援 多種語言、文件齊全;
-
有訊息 確認機制 和 持久化機制,可靠性高;
-
高度可定製的 路由;
-
管理介面 較豐富,在網際網路公司也有較大規模的應用,社群活躍度高。
(d) 缺點
-
儘管結合
Erlang
語言本身的併發優勢,效能較好,但是不利於做 二次開發和維護; -
實現了 代理架構,意味著訊息在傳送到客戶端之前可以在 中央節點 上排隊。此特性使得
RabbitMQ
易於使用和部署,但是使得其 執行速度較慢,因為中央節點 增加了延遲,訊息封裝後 也比較大; -
需要學習 比較複雜 的 介面和協議,學習和維護成本較高。
7.3. RocketMQ
RocketMQ
出自 阿里 的開源產品,用 Java
語言實現,在設計時參考了 Kafka
,並做出了自己的一些改進,訊息可靠性上 比 Kafka
更好。RocketMQ
在阿里內部被廣泛應用在 訂單,交易,充值,流計算,訊息推送,日誌流式處理,binglog
分發 等場景。
(a) 主要特性
-
基於 佇列模型:具有 高效能、高可靠、高實時、分散式 等特點;
-
Producer
、Consumer
、佇列 都支援 分散式; -
Producer
向一些佇列輪流傳送訊息,佇列集合 稱為Topic
。Consumer
如果做 廣播消費,則一個Consumer
例項消費這個Topic
對應的 所有佇列;如果做 叢集消費,則 多個Consumer
例項 平均消費 這個Topic
對應的佇列集合; -
能夠保證 嚴格的訊息順序;
-
提供豐富的 訊息拉取模式;
-
高效的訂閱者 水平擴充套件能力;
-
實時 的 訊息訂閱機制;
-
億級 訊息堆積 能力;
-
較少的外部依賴。
(b) 部署環境
RocketMQ
可以執行在 Java
語言所支援的平臺之上。使用 RocketMQ
需要:
Java JDK
- 安裝
git
、Maven
RocketMQ
安裝包
(c) 優點
-
單機 支援
1
萬以上 持久化佇列; -
RocketMQ
的所有訊息都是 持久化的,先寫入系統PAGECACHE
,然後 刷盤,可以保證 記憶體 與 磁碟 都有一份資料,而 訪問 時,直接 從記憶體讀取。 -
模型簡單,介面易用(
JMS
的介面很多場合並不太實用); -
效能非常好,可以允許 大量堆積訊息 在
Broker
中; -
支援 多種消費模式,包括 叢集消費、廣播消費等;
-
各個環節 分散式擴充套件設計,支援 主從 和 高可用;
-
開發度較活躍,版本更新很快。
(d) 缺點
-
支援的 客戶端語言 不多,目前是
Java
及C++
,其中C++
還不成熟; -
RocketMQ
社群關注度及成熟度也不及前兩者; -
沒有
Web
管理介面,提供了一個CLI
(命令列介面) 管理工具帶來 查詢、管理 和 診斷各種問題; -
沒有在
MQ
核心裡實現JMS
等介面;
7.4. Kafka
Apache Kafka
是一個 分散式訊息釋出訂閱 系統。它最初由 LinkedIn
公司基於獨特的設計實現為一個 分散式的日誌提交系統 (a distributed commit log
),之後成為 Apache
專案的一部分。Kafka
效能高效、可擴充套件良好 並且 可持久化。它的 分割槽特性,可複製 和 可容錯 都是其不錯的特性。
(a) 主要特性
-
快速持久化:可以在
O(1)
的系統開銷下進行 訊息持久化; -
高吞吐:在一臺普通的伺服器上既可以達到
10W/s
的 吞吐速率; -
完全的分散式系統:
Broker
、Producer
和Consumer
都原生自動支援 分散式,自動實現 負載均衡; -
支援 同步 和 非同步 複製兩種 高可用機制;
-
支援 資料批量傳送 和 拉取;
-
零拷貝技術(zero-copy):減少
IO
操作步驟,提高 系統吞吐量; -
資料遷移、擴容 對使用者透明;
-
無需停機 即可擴充套件機器;
-
其他特性:豐富的 訊息拉取模型、高效 訂閱者水平擴充套件、實時的 訊息訂閱、億級的 訊息堆積能力、定期刪除機制;
(b) 部署環境
使用 Kafka
需要:
Java JDK
Kafka
安裝包
(c) 優點
-
客戶端語言豐富:支援
Java
、.Net
、PHP
、Ruby
、Python
、Go
等多種語言; -
高效能:單機寫入
TPS
約在100
萬條/秒,訊息大小10
個位元組; -
提供 完全分散式架構,並有
replica
機制,擁有較高的 可用性 和 可靠性,理論上支援 訊息無限堆積; -
支援批量操作;
-
消費者 採用
Pull
方式獲取訊息。訊息有序,通過控制 能夠保證所有訊息被消費且僅被消費 一次; -
有優秀的第三方
Kafka Web
管理介面Kafka-Manager
; -
在 日誌領域 比較成熟,被多家公司和多個開源專案使用。
(d) 缺點
-
Kafka
單機超過64
個 佇列/分割槽 時,Load
時會發生明顯的飆高現象。佇列 越多,負載 越高,傳送訊息 響應時間變長; -
使用 短輪詢方式,實時性 取決於 輪詢間隔時間;
-
消費失敗 不支援重試;
-
支援 訊息順序,但是 一臺代理當機 後,就會產生 訊息亂序;
-
社群更新較慢。
7.5. 幾種訊息佇列對比
這裡列舉了上述四種訊息佇列的差異對比:
Kafka
在於 分散式架構,RabbitMQ
基於 AMQP
協議 來實現,RocketMQ
的思路來源於 Kafka
,改成了 主從結構,在 事務性 和 可靠性 方面做了優化。廣泛來說,電商、金融 等對 事務一致性 要求很高的,可以考慮 RabbitMQ
和 RocketMQ
,對 效能要求高 的可考慮 Kafka
。
小結
本文介紹了訊息佇列的特點,訊息佇列的 傳遞服務模型,訊息的 傳輸方式,訊息的 推拉模式。然後介紹了 ActiveMQ
,RabbitMQ
,RocketMQ
和 Kafka
幾種常見的訊息佇列,闡述了 各種訊息佇列 的 主要特點 和 優缺點。通過本文,對於訊息佇列及相關技術選型,相信你會有了更深入的理解和認識。更多細節和原理性的東西,還需在實踐中見真知!
歡迎關注技術公眾號: 零壹技術棧
本帳號將持續分享後端技術乾貨,包括虛擬機器基礎,多執行緒程式設計,高效能框架,非同步、快取和訊息中介軟體,分散式和微服務,架構學習和進階等學習資料和文章。