【系統架構】聊聊開源消息中間件的架構和原理

說到消息中間件,身在互聯網的童鞋們確定下意識的就是高併發,高性能io調度等浮如今腦海,可是對應用來講,可能他的做用遠不止性能這麼簡單,尤爲是對與交易,金融打交道的業務平臺來講。java

ok,下面給你們介紹一下金融交易平臺中,哪些場景是須要咱們用到消息中間件的?爲何要使用?怎麼設計中間件私有云讓開發比較爽?(鑑於不一樣同窗語言擅長不相同,這裏只聊設計原理和機制方面的內容,本文會涉及市面上流行的開源產品,如activemq、rabbitmq、kafka、metaq等)mysql

消息中間件的做用就是用來異步化併發能力的一個載體,不只如此,它仍然須要在架構上保證不少能力,高可用,高併發,可擴展,可靠性,完整性,保證順序等,光是這些都已經讓各類設計者比較頭疼了; 更有一些變態的需求,例如慢消費,不可重複等須要花的設計代價是至關高的,因此不要盲目的迷信開源大牛,對於不少機制,幾乎都要重建;創建一個符合全部業務,好用,通用的私有云,沒那麼簡單。算法

若是說一個支付系統天天要處理億級業務單的話,那麼消息中間件的處理能力至少得達到近百億,由於不少系統都是依賴於中間件的集羣能力,而且要保證不能出錯,so,讓咱們從架構的一些層面上來一點點來分析中間件是怎麼作到的?sql

高可用

高可用是一個永恆的話題,這個也是在金融界是否靠譜的一個衡量標準,要知道,金融界的架構師們會千方百計的讓數據不會丟失,哪怕是一條數據,可是事實上,這個東西從理論上來說,得靠人品。。。這個不是忽悠。數據庫

舉一個例子來講,互聯網數據架構中一份數據至少要存三份才叫高保證,可是事實上,谷歌的比利時數據中心在8.13日遭到雷劈後數據中心永久丟失0.000001%,不到0.05%的磁盤未能修復,這裏要說的是,天時地利人和很重要,極限條件下沒有什麼不可能,必定會有架構漏洞,下面看一下mq高可用的通常作法:
下圖是activemq的HA方案:
【系統架構】聊聊開源消息中間件的架構和原理

api

activemq的HA 經過master/slave的failover進行託管,其中主從切換能夠經過多種方式進行切換:
1:經過一個nfs或其它共享磁盤設備進行一個共享鎖,經過對共享文件鎖的佔有,來標記master的狀態,當m掛掉之後,對應的slave會佔有shared_lock而轉換爲master
緩存

2:經過zookeeper進行集羣的管理,比較常見,這裏再也不介紹
下圖是metaq的HA方案
【系統架構】聊聊開源消息中間件的架構和原理
如上圖,一模一樣,也是經過zk管理broker的主從結點。


服務器

固然這個只是其中的一個failover機制,只能保證消息在broker掛掉時轉換到slave上,可是不能保證在這中間過程當中的消息的丟失 網絡

當消息從broker流經時,頗有可能由於宕機或是其它硬件故障而致使後,就有可能致使消息丟失掉,這個時候,就須要有相關的存儲介質對消息的進行一個保障了數據結構

那麼咱們舉kafka的存儲機制做爲一個參考,要知道消息中間件對存儲的依賴不但要求速度快,而且要求IO的需求成本很是低,kafka本身設計了一套存儲機制來知足上述的需求,這裏簡單介紹一下。

首先kafka中的topic在分佈式部署下分作多個分區,分區的就至關於消息進行了一個負載,而後由多臺機器進行路由,舉個例子: 一個topic,debit_account_msg會切分爲 debit_account_msg_0, debit_account_msg_1, debit_account_msg_2。。。等N個partition,每一個partition會在本地生成一個目錄好比/debit_account_msg/topic

裏面的文件會分出不少segment,每一個segment會定義一個大小,好比500mb一個segment,一個file分爲index和log二個部分
00000000000000000.index
00000000000000000.log
00000000000065535.index
00000000000065535.log
其中數字表明msgId的值的索引發點,對應的數據結構以下圖:
【系統架構】聊聊開源消息中間件的架構和原理
1,0表明msgId爲1的消息,0表明在這個文件中的偏移量,讀取到這個文件後再尋找到查詢到對應的segment log文件讀取對應的msg信息,對應的信息是一個固定格式消息體:
【系統架構】聊聊開源消息中間件的架構和原理
顯然,這種機制單純應用確定是不能知足高併發IO的,首先二分查找segmentfile,而後再經過offset找到對應數據,再讀取msgsize,再讀取報體,至少是4次磁盤io,開銷較大,可是在拉取時候是使用的順序讀取,基本上影響不大。








除了要上面所說的查詢外。其實在寫入磁盤以前都是在os上的pagecache上進行讀寫的,而後經過異步線程對硬盤進行定時的flush(LRU策略),但其實這個風險很大的,由於一旦os宕掉,會致使數據的丟失,尤爲是在進行慢消費多積壓不少數據的狀況下,可是kafka他弟metaq對這塊已經作了不少改造,對這些分區文件進行了replication機制(阿里內部使用),因此在這個層面上再怎麼遭雷劈丟消息的機率就會比較小了,固然也不排除主機房光纜被人挖掉會有什麼樣的狀況發生。

說了這麼多,彷佛看起來的比較完美和美好,可是實際上運維成本彷佛很大。由於這些都是文件,一旦發生問題,須要人工去處理起來至關麻煩,並且是在一臺一臺機器上,須要比較大的運維成本去作一些運維規範以及api調用設施等。

因此,在這塊咱們能夠經過改造,將數據存儲在一些nosql上,好比mongoDB上,固然mysql也是能夠,可是io能力和nosqldb徹底不在一個水平線上,除非咱們有強烈的事務處理機制,而在金融裏的確對這塊要求比較至關嚴謹。像在支付寶後面就使用了metaq,由於以前的中間件tbnotify在處理慢消費的狀況下會很被動,而metaq在這塊會有極大的優點,爲何,請聽後面分解。

高併發

最開始你們使用mq很大部分工程師都用於解決性能和異步化的問題,其實對於同一個點來講,一個io調度其實並非那麼耗資源,廢話少說讓咱們看下mq裏的一些高併發點,首先在這裏先介紹一下幾個比較有名的中間件背景:

activemq當時就是專門的企業級解決方案,遵照jee裏的jms規範,其實性能也仍是不錯的,可是拉到互聯網裏就是兔子抱西瓜,無能爲力了

rabbitmq採用erlang語言編寫,遵照AMQP協議規範,更具備跨平臺性質,模式傳遞模式要更豐富,而且在分佈式

rocketmq(metaq3.0現今最新版本, kafka也是metaq的前身,最開始是linkedIn開源出來的日誌消息系統 ),metaq基本上把kafka的原理和機制用java寫了一遍,通過屢次改造,支持事務,發展速度很快,而且在阿里和國內有很比較好的社區去作這塊的維護 。

性能比較,這裏從網上找一些數據,僅供參考:
【系統架構】聊聊開源消息中間件的架構和原理

說實話來說,這些數據級別來說,相差沒有太離譜,可是咱們能夠經過分析一些共性來說,這些主要性能差異在哪裏?
rocketmq是metaq的後繼者,除了在一些新特性和機制方面有改進外,性能方面的原理都差很少,下面說下這些高性能的一些亮點:

  • rocketmq的消費主要採用pull機制,因此對於broker來說,不少消費的特性都不須要在broker上實現,只須要經過consumer來拉取相關的數據便可,而像activemq,rabbitmq都是採起比較老的方式讓broker去dispatch消息,固然些也是jms或amqp的一些標準投遞方式

  • 文件存儲是順序存儲的,因此來拉消息的時候只須要經過調用segment的數據就能夠了,而且consumer在作消費的時候是最大程度的去消費信息,不太可能產生積壓,並且能夠經過設置io調度算法,像noop模式,能夠提升一些順序讀取的性能 。

  • 經過pagecache去命中在os緩存中的數據達到一個熱消費

  • metaq的批量磁盤IO以及網絡IO,儘可能讓數據在一次io中運轉,消息起來都是批量的,這樣對io的調度不太須要消耗太多資源

  • NIO傳輸,以下圖,這個是最初metaq的一個架構,最初metaq使用的是taobao內部的gecko和notify-remoting集成的一些高性能的NIO框架去分發消息
    【系統架構】聊聊開源消息中間件的架構和原理
  • 消費隊列的輕量化,要知道咱們的消息能力是經過隊列來獲取的

看下面的圖:
【系統架構】聊聊開源消息中間件的架構和原理
metaq在消費的物理隊列上添加了邏輯隊列,隊列對應的磁盤數據是串行化的,隊列的添加不會添加磁盤的iowait負擔,寫入能夠順序,可是在讀取的時候仍然須要去用隨機讀,首先是邏輯隊列 ,而後再讀取磁盤,因此pagecache很重要,儘可能讓內存大一些,這塊分配就會充分獲得利用。

其實作到上面這些已經基本上能保證咱們的性能在一個比較高的水平; 可是有時候性能並非最重要的,最重要的是要和其它的架構特性作一個最佳的平衡,畢竟還有其它的機制要知足。由於在業界基本上最難搞定的三個問題:高併發,高可用,一致性是互相沖突的。

可擴展

這是一個老生常談的問題,對於通常系統或是中間件,能夠較好的擴展,可是在消息中間件這塊,一直是一個麻煩事,爲何?

先說下activemq的擴展起來的侷限性,由於activemq的擴展須要業務性質,做爲broker首先要知道來源和目的地,可是這些消息若是都是分佈式傳輸的話,就會變的複雜,下面看一下activemq的負載是怎麼玩轉的
【系統架構】聊聊開源消息中間件的架構和原理
咱們假設producer去發topicA的消息,若是正常狀況下全部的consumer都連到每個broker上的,辣麼假如broker上有producer上的消息過來,是能夠transfer到對應的consumer上的。
可是若是像圖中 broker2中若是沒有對應的消息者鏈接到上面,這種狀況下怎麼辦呢?由於假設同一個topic的應用系統(producer)和依賴系統 (consumer)節點不少,那又該如何擴容呢?activemq是能夠作上圖中正常部分,可是須要改變producer,broker,consumer的對應的配置,至關麻煩。
固然activemq也能夠經過multicast的方式來作動態的查找(也有人提到用lvs或f5作負載,可是對於consumer同樣存在較大的問題,並且這種負載配置對於topic的分發,沒實質性做用),可是,仍然會有我說的這個問題,若是topic太大,每一個broker都須要鏈接全部的producer或是consumer, 否則就會出現我說的狀況,擴容這方面activemq是至關的麻煩



下面來講一下metaq是如何作這塊事情的,看圖說話
【系統架構】聊聊開源消息中間件的架構和原理
metaq上是以topic爲分區的,在這個層面來說,咱們只要配置topic的分區有多少個就行了,這樣切片起來就是有個"業務"概念做爲路由規則;通常一個broker機器上配置有多個topic,每一個topic在一個機器上通常是隻有一個分區,假如機器不夠了,也是能夠支持多個分區的,通常來講,咱們能夠經過業務id來取模自定義分區,經過獲取發區參數便可。
【系統架構】聊聊開源消息中間件的架構和原理
metaq的消費者也是經過group(這個分組通常根據partition的能力來配置)負載的方式去partition去拉消息,假若有多的消費者,不須要參與消費。通常線上都是這種狀況,由於畢竟應用服務器要遠大於消息服務器。
【系統架構】聊聊開源消息中間件的架構和原理
另一種狀況,當分區過多時,以下圖
【系統架構】聊聊開源消息中間件的架構和原理
這樣的負載在多依賴的核心消息時,對於服務器broker的要求仍是比較高的,畢竟依賴的量較大,另外對於消息具備廣播特色的話,可能會更大,因此對於broker而言,須要高io的硬盤以及大內存作pagecache,真正須要的運算並不須要太大
【系統架構】聊聊開源消息中間件的架構和原理








可靠性

可靠性是消息中間件的重要特性,看下mq是怎麼流轉這些消息的,拿activemq來先來作下參考,它是基於push&push機制。

如何保證每次的消息發送都被消費到?Activemq的生產者發送消息後都須要收到一條broker的ack纔會確認消收到,一樣對於broker到consumer也是一樣的保障。

Metaq的機制也是一樣的,可是broker到consumer是經過pull的方式,因此它的到達保障要看consumer的能力如何,可是通常狀況下,應用服務器集羣不太可能出現雪崩效應。

如何保證消息的冪等性?目前來講基本上activemq,metaq都不能保證消息的冪等性,這就須要一些業務來保證了。由於一旦broker超時,就會重試,重試的話都會產生新的消息,有可能broker已經落地消息了,因此這種狀況下無法保證同一筆業務流水產生二條消息出來

消息的可靠性如何保證?這點上activemq和metaq基本上機制同樣:
生產者保證:生產數據後到broker後必需要持久化才能返回ACK給來源
broker保證:metaq服務器接收到消息後,經過定時刷新到硬盤上,而後這些數據都是經過同步/異步複製到slave上,來保證宕機後也不會影響消費
activemq也是經過數據庫或是文件存儲在本地,作本地的恢復


消費者保證:消息的消費者是一條接着一條地消費消息,只有在成功消費一條消息後纔會接着消費下一條。若是在消費某條消息失敗(如異常),則會嘗試重試消費這條消息(默認最大5次),超過最大次數後仍然沒法消費,則將消息存儲在消費者的本地磁盤,由後臺線程繼續作重試。而主線程繼續日後走,消費後續的消息。所以,只有在MessageListener確認成功消費一條消息後,meta的消費者纔會繼續消費另外一條消息。由此來保證消息的可靠消費。

一致性

mq的一致性咱們討論二個場景:
1:保證消息不會被屢次發送/消費

2:保證事務
剛纔上面介紹的一些mq都是不能保證一致性的,爲何不去保證?代價比較大,只能說,這些都是能夠經過改造源碼來進行保證的,並且方案比較相對來講不是太複雜,可是額外的開銷比較大,好比經過額外的緩存集羣來保證某段時間的不重複性,相信後面應該會有一些mq帶上這個功能。

Activemq支持二種事務,一個是JMS transaction,一個是XA分佈式事務,若是帶上事務的話,在交互時會生成一個transactionId去到broker,broker實現一些TM去分配事務處理,metaq也支持本地事務和XA,遵照JTA標準這裏activemq和metaq的事務保證都是經過redo日誌方式來完成的,基本上一致。

這裏的分佈式事務只在broker階段後保證,在broker提交以前會把prepare的消息存儲在本地文件中,到commit階段纔將消息寫入隊列,最後經過TM實現二階段提交。

小結

像公司內部也有一些性能很不錯的消息中間件,但願後面也能作到開源給到更多的人去使用。針對如今流行的一些消息中間件咱們能夠針對不一樣的應用,不一樣的成本,不一樣的開發定製不一樣的架構,固然,這些架構必定是須要咱們通過多方面考量。

推薦閱讀:

精心整理 | 2017下半年文章目錄
關於緩存和數據庫強一致的可行方案
用戶進程緩衝區和內核緩衝區
經過金礦故事介紹動態規劃(上)


專一服務器後臺技術棧知識總結分享

歡迎關注交流共同進步

【系統架構】聊聊開源消息中間件的架構和原理

碼農有道 coding

碼農有道,爲您提供通俗易懂的技術文章,讓技術變的更簡單!

相關文章
相關標籤/搜索