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