日誌與消息隊列前端
消息隊列的應用價值算法
數據集成與系統解耦數據庫
異步處理與事件驅動後端
流量削峯網絡
事務消息與分佈式事務的最終一致數據結構
從歷史看消息隊列的價值演化架構
小米的消息隊列產品Talos與EMQ併發
Talos/EMQ 與開源產品的區別app
Talos與EMQ的區別負載均衡
後續文章
參考文獻
時常會思考消息隊列的價值是什麼?新人加入團隊後該如何理解消息隊列?又該如何理解小米的自研產品 Talos 和 EMQ?
鑑於這些考慮,我把對消息隊列的理解作一個簡單總結,但願能幫助感興趣的同窗瞭解 Talos/EMQ 的價值和定位,以及它在企業架構中扮演着什麼樣的角色。
做者批註:思考手上的工做,找到它的價值和定位,就找到了工做的目標 — 努力將價值最大化
說到消息隊列,就不得不說一下日誌
做者批註:本質上日誌與消息隊列均可以抽象成 Pub/Sub 的模式
Jay Kreps (Confluent CEO,Kafka 核心做者) 在《The Log: What every software engineer should know about real-time data's unifying abstraction》[1] 中系統性描述了日誌的價值和重要性,指出了日誌特定的應用目標:它記錄了什麼時間發生了什麼事情(they record what happened and when)。而這,正是分佈式系統許多問題的核心。
做者批註:《日誌:每一個軟件工程師都應該知道的有關實時數據的統一抽象》聽說是史詩般必讀文章
這個「按時間天生有序」的特性讓日誌解決了分佈式系統中的兩個重要問題:
修改操做的排序和數據分發(ordering changes and distributing data),這爲併發更新的一致性和副本複製提供了基礎。分佈式系統中爲了保證各副本的一致性,協商出一致的修改操做順序是很是關鍵且核心的問題,利用日誌天生有序的特性能夠將這個複雜的問題變得簡單。
咱們來看一個不太嚴謹的例子:假設系統有三個副本,都存儲着 A=1 的初始值,某一時刻,要執行一個加法乘法的操做序列對 A 的值進行修改:"+1"、"*3"
假設 Primary 收到兩條指令後,對其餘副本依次廣播了 "+1"、"*3",因爲網絡的不肯定因素,第一個副本收到的指令爲 "*3"、"+1",第二個副本收到的指令爲 "+1"、"*3",這就會帶來副本的一致性問題;
如何解決這個問題呢?答案是日誌,利用日誌將併發更新進行排序,全部副本從日誌中按順序讀取更新操做,應用到本地,就能夠將這個複雜的問題簡單化。
如圖,Primary 依次進行 "+1"、"*3" 的操做,並寫入日誌,利用日誌作修改操做的「數據分發」,使得各副本可以在本地應用徹底相同的操做序列,從而保證各副本數據的一致;
做者批註:本質上是是把多臺機器一塊兒執行同一件事情的問題簡化爲實現分佈式一致性日誌,經過日誌的 Pub/Sub 保證多臺機器對數據處理的最終一致
上面的例子能很好的說明爲何順序是保證副本間一致性的關鍵,而日誌爲此提供了基礎和載體。讓咱們進一步思考和聯想:
Primary 將各類操做經過日誌序列化,各 Replica 從日誌中讀取並應用到本地狀態,
這種解決問題的模式也叫 Pub/Sub,即抽象成通用的數據訂閱機制,而
將這種抽象產品化,就是消息隊列。
消息隊列做爲大型分佈式系統的關鍵組件,在實時數據或流式數據架構中扮演着重要角色,它一般被應用在系統解耦、異步處理、流量削峯,分佈式事務/金融電商等場景中,接下來咱們分別從這幾個場景淺談消息隊列的應用價值。
1. 數據集成與系統解耦
若是說日誌爲解決分佈式一致性問題提供了基礎,那麼一樣是 Pub/Sub 模式的消息隊列,則爲琳琅滿目的數據系統之間協做提供了一件利器,大大簡化了數據集成的複雜度(O(N^2) 變爲 O(N)),提高了數據流轉的效率,加速了數據價值展示;
什麼是數據集成?引用 Jay Kreps 的文章:
Data integration is making all the data an organization has available in all its services and systems.
數據集成即將一個組織所擁有的數據,使其在全部的服務和系統中均可用。
那麼數據集成是解決什麼問題?使用消息隊列又是如何加速數據集成的?咱們看一個案例
很多業務都有這樣的場景:隨着業務量的爆發,爲理解用戶行爲,須要收集日誌保存到 Hadoop 上作離線分析;爲了能方便定位問題,同時把日誌導一份到 ElasticSearch 作全文檢索;爲了給老闆查看業務情況,須要將數據彙總到數倉中提供統計報表;此外還須要進行一些實時的流式計算...一個業務系統須要同時與多個大數據系統交互,一段時間後,團隊有了新的業務,新業務系統又重複上面的事情,對接各類系統,以下圖,最終的結果是系統架構盤根交錯,快樂與痛苦齊飛。

能夠看到,上面案例本質上是一個數據集成的需求,但數據集成的難處就在於須要面對:
1)愈來愈多的數據;2)愈來愈多的數據系統;
爲何會有這種現象,只能說大數據時代下,很難有一個系統可以解決全部的事情,這就致使了業務數據由於不一樣用途而須要存入不一樣的系統,好比上面說的檢索,分析,計算等;因而,數據集成的挑戰就變成了不一樣系統間繁雜的數據同步,有多複雜?M 個業務,N 個數據系統:
1)全部業務都須要關心到每個系統的數據導入,複雜度 M*N
2)架構複雜交錯,各個鏈路容易互相影響。好比一個業務數據寫入 A 系統失敗,通常會影響 B 系統的寫入;
3)出現問題後很難定位,且容易丟失數據,數據恢復也變得困難
如何解決上面這些問題,從而提升集成的效率呢?仍是 Pub/Sub 的思路,引入消息隊列作數據分發,架構以下:

這樣的架構業務只須要關心如何將數據導入消息隊列便可,好處不只如此:
1)因爲消息隊列的解耦,架構複雜度大大下降,各系統間的數據同步不會互相影響(各系統無需知道彼此的存在);
2)因爲消息隊列的持久化,不易丟失數據,也能夠在必要時進行數據重放;
3)因爲全部數據都彙總到消息隊列中,數據集成很是高效,能夠快速接入新的數據系統而不影響存量架構
做者批註:解耦是消息隊列解決的最本質的問題,價值所在。
如此,咱們看到消息隊列在提升數據集成效率上起到了關鍵做用,實際上,基於消息隊列構建一個穩定可靠的、鏈接全部數據系統的數據流是很是關鍵的,小米數據流系統即是在作這樣的事情;借用 Jay Kreps 的話來講明數據集成平臺的重要性:
不少組織沒有完整可靠的數據流基礎平臺,卻在過多的關注上層數據模型,這是本末倒置的表現
.
做者批註:作好消息隊列和數據集成平臺的重要性不言而喻,價值所在。
2. 異步處理與事件驅動
異步處理本質上是解耦系統間的 RPC 調用,將那些依賴其餘系統,同時對時效性要求不是很高的邏輯剝離出來,提升系統吞吐和響應時間,下降系統耦合的複雜度;異步處理注重的是「通知」,事件驅動;
做者批註:可以異步處理的邏輯通常是沒必要在當時那個時間點拿到結果,有個通知就行
舉一個例子來講明,用戶註冊爲了驗證真實性,會向註冊郵箱發送驗證郵件,只有驗證過的用戶才能行使註冊用戶的權利。同步的處理/調用過程通常以下:
採用同步調用的問題是:一方面用戶看到註冊成功須要等待發送郵件成功,延長了整個流程的耗時;另外一方面若是發送郵件失敗會影響整個註冊流程;
對於註冊流程來講,系統不須要等待發送郵件成功才告知用戶註冊成功,即發送郵件是否成功這個結果在註冊的時間點並非必需要拿到的,註冊邏輯只須要「通知」下游產生了一個註冊事件便可;
引入消息隊列異步處理的流程以下,發送驗證郵件的動做由訂閱消息隊列的下游系統/邏輯來處理:
使用消息隊列異步處理的好處是:
1)簡化業務邏輯,更快的返回結果,提高響應效率和系統吞吐,改善用戶體驗;
2)即便異步邏輯的郵件系統出問題,也不會影響註冊流程,下降系統耦合度。
上述案例是一個異步處理很是典型的問題,同時也足夠簡單,在一些複雜的場景中好比電商,引入消息隊列的做用就更加明顯了,好比用戶下單後,須要將訂單信息推給配送系統安排配貨和物流,須要短信通知用戶下單成功,須要發送給大數據系統作統計分析,須要給推薦系統作計算 … 若是採用同步處理:
1)用戶的下單流程會很是長,且任何一個系統有問題都會致使下單失敗;
2)隨着業務複雜度提升,要適配和接入的系統會愈來愈多,這幾乎是反人類的;異步處理可讓問題變得簡單,以下圖,既能讓下游系統實時的處理訂單信息,也能簡化架構複雜度,提高下單效率。
異步處理更多的是事件驅動,引入消息隊列作事件的轉述與投遞,從架構上也會達到解耦的功效;這種應用到處可見,除去上面說的準實時的場景,還有相似保險投保有一個猶豫期,保單的延時生效(延時隊列)這種延遲處理場景;事實上,隨着雲原生技術的發展,Serverless 日益流行,Serverless 的核心是事件驅動,而事件驅動的核心是一套穩定、靠譜的 Event-Streaming 系統:消息隊列。
做者批註:大型分佈式架構的數據總線、微服務中隔離直連的異步調用,Serverless 的事件驅動,到處都有消息中間件的身影,可謂價值所在
3. 流量削峯
電商大促、秒殺、節假日搶票等場景都是對系統服務的巨大挑戰,這類場景的特色就是瞬時併發的流量 / QPS 很是高(海量用戶同一時間訪問),很容易將後端服務打掛。
爲何要削峯?歸根結底主要是兩個緣由
1)後端服務的處理能力每每是有限的,沒法跟前端對等,好比電商後臺的數據庫,好比短信系統的網關,一樣的機器可以支撐的併發量沒法跟前端處在同一個數量級,從成本考慮也沒法無限制擴展
2)後端服務對到達自身的流量是不可控的,只要上游調用不限流,後端就隨時暴露在流量洪峯的壓力下
消息隊列之因此能承擔削峯的角色,也是由於能解決上面的問題:
1)消息隊列高吞吐的特性能夠在不高的成本下承接住巨大的流量,起到蓄水池的做用,攔截上游洪水
2)後端採用拉取的模式消費,能夠按照自身的處理能力進行流控,平穩地從隊列中消費,從而達到保護自身,避免被壓垮的目的
4. 事務消息與分佈式事務的最終一致
消息隊列中的事務是什麼?解決什麼問題?
消息隊列中的事務是指
「業務執行本地事務」 與 「消息發送到消息隊列」 的原子性。事務消息跟普通消息的區別是,在事務提交前,這個消息對消費者來講是不可見的;基於事務消息,消息隊列最終解決的是生產者與消費者在分佈式事務場景中的一致性問題。
做者批註:消費端對數據的處理邏輯通常是冪等的
例如在電商系統中,爲了提升效率,下降系統耦合,一般會對訂單流作異步拆分,但有時異步處理的某個事件須要上下游系統保持一致,這就帶來了分佈式事務的問題。
咱們以小米有品的一個真實場景「優惠券覈銷」來講明,雙十一和米粉節有品發放了一堆優惠券,使用優惠券購物下單成功後,被使用的優惠券就要刪掉,若是下單沒成功,優惠券就不該被刪掉,即下單成功與優惠券覈銷要保持一致。
如上,引入消息隊列作異步處理,但使用非事務消息可能會有以下狀況:
1)建立訂單成功,寫消息失敗致使使用的優惠券沒有被刪掉;
2)訂單因支付失敗沒有建立成功,優惠券卻被刪除了;
做者批註:之後用券購物,能夠洞察背後系統的事務邏輯是否是靠譜了 :-)
這就是事務消息要解決的問題。即保證「業務執行本地事務」(建立訂單)和「發送消息」這兩件事情的原子性,而後依賴消息隊列的可靠投遞,實現訂單系統與優惠券系統對數據處理的最終一致;事務語義的流程以下圖:

服務端爲了實現事務機制(保障下游系統能消費到客戶端認爲已經提交的事務消息),須要作如下工做:
1)引入事務狀態來
「記錄」客戶端事務是否提交
2)因爲網絡緣由或訂單系統重啓,致使有些客戶端已提交或撤回的消息在服務端的狀態是未知的,須要輔助
「補償」機制(對不肯定狀態的消息進行回查或客戶端詢問)來解決
Talos 系統在實現事務機制時有兩點須要說明:
1)關於「補償」機制的實現:RocketMQ 爲業務提供了一種反查本地事務狀態接口的方案,而 Talos 則使用了一種成本更爲低廉的方法,具體可期待後續文章《Talos 事務消息設計》;
2)優惠券系統消費消息失敗,因爲 Talos 對消息的持久化和可重放,即便優惠券覈銷失敗,也能夠經過重試來解決;
簡單總結下分佈式事務場景中消息隊列的價值:經過
兩階段提交語義(事務消息),事務狀態反查的補償機制,消息隊列的可靠投遞,以及業務消費邏輯的冪等,實現瞭如上所述分佈式事務的最終一致。
消息中間件做爲企業基礎架構的關鍵組件由來已久,跟 Google 的三駕馬車同樣,同處於大數據時代的開啓;十幾年來,消息系統的演化歷程,大體能夠分爲三個階段[2]。從這三個階段的歷史來看,咱們基本能夠獲得消息隊列在大數據架構變遷過程當中的角色和價值演化。
最先的消息隊列開源產品 ActiveMQ,要追溯到 2003 年,這一時代的 MQ 產品,主要是圍繞 JMS、AMQP 等標準化消息規範和協議來設計的,其目標主要是用來減輕經典 RPC 調用的緊耦合,剝離異步處理邏輯,解耦系統複雜度;
價值關鍵詞:調用鬆耦合、異步處理
消息系統第二個階段即是以 Kafka 爲經典表明的實時數據管道時代,這一時代的產品在吞吐和數據量上都有質的提高,其主要的應用場景是解決數據集成的痛點,提升數據在不一樣系統間的流轉效率。同時,Lambda 架構的引入使得 Kafka + Storm 成爲那個時代實時計算的標配。
價值關鍵詞:數據管道、數據集成、實時計算
消息系統的第三個階段,流數據平臺,不只僅是一個簡單的數據管道,更多的強調平臺化。平臺化會帶來更多的挑戰與困難,如業務多樣性,數據海量化等,這就使得消息系統必須配備完善的多租戶管理、I/O隔離、流控與配額管理等。隨着容器化的發展,消息隊列更須要一個靈活的架構來適應這種變革,存儲計算分離極可能是中間件產品將來架構的發展方向。仔細想來,在雲計算方面,不管是早期的 MapReduce 仍是最近的 Spark-Streaming、Flink,都是存儲計算分離的架構,計算層只負責「計算」,而數據的存儲都依賴 HDFS 等存儲系統進行「讀取與寫入」。
價值關鍵詞:平臺化、容器化、存儲計算分離架構
聊了這麼多消息隊列的「價值」,顯然,
小米消息隊列產品的使命即是把這些「價值」落地到業務。
做者批註:這就是工做的目標啊,工做的目標啊,工做的目標啊!重要的事情說三遍
小米有兩款自研的消息中間件產品:Talos 與 EMQ;篇幅限制,這裏簡單回答兩個常常被小夥伴問到的問題:
1.Talos/EMQ 與開源產品 Kafka/ActiveMQ/RocketMQ 等有什麼區別?
2.Talos 和 EMQ 之間又有什麼區別?
1. Talos/EMQ 與開源產品的區別
Talos 和 EMQ 均立項於 2015 年,前者基於 HDFS,後者基於 HBase,得益於小米雲平臺在分佈式存儲方面的深耕與積累,這兩款產品都獲得了存儲團隊的大力支持,伴隨業務的信任與錘鍊而來,具體可參考後續文章《萬億級消息背後:小米在消息隊列的實踐》。
要說與開源產品的聯繫,一方面在系統設計上會針對開源產品的一些痛點進行優化,另外一方面也會不斷學習/借鑑開源產品的優勢落地內部業務;主要的區別主要有兩方面:
1)企業級特性與平臺化
Talos 與 EMQ 立項的目標即是服務於小米內部業務和生態鏈公司,對內打通 LADP,服務各部門業務,對外打通小米帳號,向生態鏈公司輸出中間件價值。針對企業深度定製的多租戶管理以及一些平臺化特性是它們與開源產品在落地點上的主要不一樣。
2)架構設計的不一樣
Talos 和 EMQ 架構上皆是存儲計算分離的設計,從架構理念上相對領先於業界同時期的開源產品,存儲計算分離的好處有不少,主要兩個方面:
固然,這種架構也不是完美無瑕,
系統設計歷來都是實際狀況和業務特色綜合衡量取捨的結果,不存在完美的架構和設計,只有合適的架構與設計。
2. Talos與EMQ的區別
Talos 和 EMQ 的區別,
更像是 「從歷史看消息隊列價值演化」 小節中的 「數據管道」 與 「異步調用解耦」 的區別。
從消息隊列的使用需求或消費模型來講,通常分爲兩種類型的設計:一種是面向流而設計的,如 Kafka;一種是面向隊列去設計的,即傳統意義的 Queue 數據結構,如 RabbitMQ;Talos 是前者,EMQ 偏向於後者;
簡單列舉下這兩種設計的特色:
流模型(Stream)
-
強調消息的有序,消費下游通常關注消息順序
-
強調消費的獨佔,Consumer 是跟 Partition 強綁定的
-
消息的讀寫通常是 Batch 進行,batch ack,偏重吞吐的優化
-
消息持久化保存,支持消息回溯重放
-
典型場景:數據集成下的系統解耦;數據收集 + 後端計算,這也是
Talos 主要的應用場景
隊列模型(Queue)
-
採用無序的方式消費,消費下游通常不關心消息排序
-
採用共享的方式消費,能夠經過增長 Consumer 數量來增長併發度
-
對於一條消息,多個 Consumer 中只有一個(多是任何一個)能消費到這條消息
-
可以跟蹤單條消息狀態,能夠隨機 ACK 一條消息,偏重延遲優化
-
通常狀況下,消息消費後就會被標記刪除
-
典型場景:異步調用,事件通知,好比上面提到的註冊時異步的發送郵件、下單時異步的發送短信,這些都是
EMQ 的應用場景
到這裏,本文一開始提出的問題,你們獲得了嗎?歡迎討論和提出建議,給耐心看完的你點個贊!
本文是流式平臺系列文章的第一篇正文,後續會針對流式平臺的技術棧(消息中間件、數據流、流式計算框架)陸續推出系列文章,歡迎你們關注與討論;
其中,消息中間件系列文章 RoadMap 這裏先給出一些預告,歡迎討論和提出感興趣的話題:
-
《消息隊列價值思考》
-
《萬億級消息背後–小米在消息隊列的實踐》
-
《Talos 負載均衡實踐》
-
《Talos GC 優化歷程》
-
《Talos 的一致性模型》
-
《Talos 事務消息設計》
-
《EMQ 架構設計實踐》
-
《Talos 快速收斂的 Consumer-Rebalance 機制》
-
《Talos Replication 設計與應用》
-
《消息隊列的存儲設計對比》
-
《消息隊列設計的難點與取捨》
[1]《The Log: What every software engineer should know about real-time data's unifying abstraction》
https://engineering.linkedin.com/distributed-systems/log-what-every-software-engineer-should-know-about-real-time-datas-unifying
[2]《槓上 Spark、Flink?Kafka 爲什麼轉型流數據平臺》
https://www.infoq.cn/article/l*fg5StAPoKiQULat0SH
本文原創「小米雲技術」,如需轉載請標明出處!