消息中間件的應用場景

 

  提升系統性能首先考慮的是數據庫的優化,可是數據庫由於歷史緣由,橫向擴展是一件很是複雜的工程,全部咱們通常會盡可能把流量都擋在數據庫以前。html

無論是無限的橫向擴展服務器,仍是縱向阻隔到達數據庫的流量,都是這個思路。阻隔直達數據庫的流量,緩存組件和消息組件是兩大殺器。這裏就重點說說MQ的應用場景。前端

 

1. MQ簡介

  MQ:Message queue,消息隊列,就是指保存消息的一個容器。具體的定義這裏就不相似於數據庫、緩存等,用來保存數據的。固然,與數據庫、緩存等產品比較,也有本身一些特色,具體的特色後文會作詳細的介紹。java

如今經常使用的MQ組件有activeMQ、rabbitMQ、rocketMQ、zeroMQ,固然近年來火熱的kafka,從某些場景來講,也是MQ,固然kafka的功能更增強大,雖然不一樣的MQ都有本身的特色和優點,可是,無論是哪一種MQ,都有MQ自己自帶的一些特色,下面,我們就先聊聊MQ的特色。git

 

2. MQ特色

l  先進先出
不能先進先出,都不能說是隊列了。消息隊列的順序在入隊的時候就基本已經肯定了,通常是不需人工干預的。並且,最重要的是,數據是隻有一條數據在使用中。 這也是MQ在諸多場景被使用的緣由。web

l  發佈訂閱
發佈訂閱是一種很高效的處理方式,若是不發生阻塞,基本能夠當作是同步操做。這種處理方式能很是有效的提高服務器利用率,這樣的應用場景很是普遍。spring

l  持久化
持久化確保MQ的使用不僅是一個部分場景的輔助工具,而是讓MQ能像數據庫同樣存儲核心的數據。數據庫

l  分佈式
在如今大流量、大數據的使用場景下,只支持單體應用的服務器軟件基本是沒法使用的,支持分佈式的部署,才能被普遍使用。並且,MQ的定位就是一個高性能的中間件。緩存

 

3. 應用場景

  消息隊列中間件是分佈式系統中重要的組件,主要解決應用解耦,異步消息,流量削鋒等問題,實現高性能,高可用,可伸縮和最終一致性架構。目前使用較多的消息隊列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ服務器

 

3.1. 消息中間件監控

Activemq 監控網絡

Rabbitmq 監控

Kafka 監控

 

3.2. 異步處理

場景說明:用戶註冊後,須要發註冊郵件和註冊短信。傳統的作法有兩種 1.串行的方式;2.並行方式
a、串行方式:將註冊信息寫入數據庫成功後,發送註冊郵件,再發送註冊短信。以上三個任務所有完成後,返回給客戶端。

 

 

b、並行方式:將註冊信息寫入數據庫成功後,發送註冊郵件的同時,發送註冊短信。以上三個任務完成後,返回給客戶端。與串行的差異是,並行的方式能夠提升處理的時間

 

 

  假設三個業務節點每一個使用50毫秒鐘,不考慮網絡等其餘開銷,則串行方式的時間是150毫秒,並行的時間多是100毫秒。
由於CPU在單位時間內處理的請求數是必定的,假設CPU1秒內吞吐量是100次。則串行方式1秒內CPU可處理的請求量是7次(1000/150)。並行方式處理的請求量是10次(1000/100)
小結:如以上案例描述,傳統的方式系統的性能(併發量,吞吐量,響應時間)會有瓶頸。如何解決這個問題呢?

 

引入消息隊列,將不是必須的業務邏輯,異步處理。改造後的架構以下:

 

 

  按照以上約定,用戶的響應時間至關因而註冊信息寫入數據庫的時間,也就是50毫秒。註冊郵件,發送短信寫入消息隊列後,直接返回,所以寫入消息隊列的速度很快,基本能夠忽略,所以用戶的響應時間多是50毫秒。所以架構改變後,系統的吞吐量提升到每秒20 QPS。比串行提升了3倍,比並行提升了兩倍。

 

3.3. 應用解耦

場景說明:用戶下單後,訂單系統須要通知庫存系統。傳統的作法是,訂單系統調用庫存系統的接口。以下圖:

 


傳統模式的缺點:假如庫存系統沒法訪問,則訂單減庫存將失敗,從而致使訂單失敗,訂單系統與庫存系統耦合

如何解決以上問題呢?引入應用消息隊列後的方案,以下圖:

 


訂單系統:用戶下單後,訂單系統完成持久化處理,將消息寫入消息隊列,返回用戶訂單下單成功
庫存系統:訂閱下單的消息,採用拉/推的方式,獲取下單信息,庫存系統根據下單信息,進行庫存操做
假如:在下單時庫存系統不能正常使用。也不影響正常下單,由於下單後,訂單系統寫入消息隊列就再也不關心其餘的後續操做了。實現訂單系統與庫存系統的應用解耦

 

3.4. 流量削峯

流量削鋒也是消息隊列中的經常使用場景,通常在秒殺或團搶活動中使用普遍。
應用場景:秒殺活動,通常會由於流量過大,致使流量暴增,應用掛掉。爲解決這個問題,通常須要在應用前端加入消息隊列。
a、能夠控制活動的人數
b、能夠緩解短期內高流量壓垮應用

 


用戶的請求,服務器接收後,首先寫入消息隊列。假如消息隊列長度超過最大數量,則直接拋棄用戶請求或跳轉到錯誤頁面。
秒殺業務根據消息隊列中的請求信息,再作後續處理。

 

3.5. 消息通信

消息通信是指,消息隊列通常都內置了高效的通訊機制,所以也能夠用在純的消息通信。好比實現點對點消息隊列,或者聊天室等。
點對點通信:

 


客戶端A和客戶端B使用同一隊列,進行消息通信。

聊天室通信:

 


客戶端A,客戶端B,客戶端N訂閱同一主題,進行消息發佈和接收。實現相似聊天室效果。

以上實際是消息隊列的兩種消息模式,點對點或發佈訂閱模式。模型爲示意圖,供參考。

具體例子能夠參考官網:https://www.rabbitmq.com/web-stomp.html

 

 

 

3.6. 海量數據同步(日誌)

日誌處理是指將消息隊列用在日誌處理中,好比Kafka的應用,解決大量日誌傳輸的問題。架構簡化以下

 


日誌採集客戶端,負責日誌數據採集,定時寫受寫入Kafka隊列
Kafka消息隊列,負責日誌數據的接收,存儲和轉發
日誌處理應用:訂閱並消費kafka隊列中的日誌數據 

 

eg:日誌收集系統

 

 

分爲Zookeeper註冊中心,日誌收集客戶端,Kafka集羣和Storm集羣(OtherApp)四部分組成。
Zookeeper註冊中心,提出負載均衡和地址查找服務
日誌收集客戶端,用於採集應用系統的日誌,並將數據推送到kafka隊列
Kafka集羣:接收,路由,存儲,轉發等消息處理
Storm集羣:與OtherApp處於同一級別,採用拉的方式消費隊列中的數據

 

n  網易使用案例:

網易NDC-DTS系統在使用,應該是最典型的應用場景,主要就是binlog的同步,數據表的主從複製。簡單一點就是:MySQL進程寫binlog文件 -> 同步應用去實時監控binlog文件讀取發送到Kafka -> 目標端處理binlog  。原理上與阿里開源的canal, 點評的puma大同小異。

 

3.5. 任務調度

參考延時隊列

 

3.7. 分佈式事物

分佈式事務有強一致,弱一致,和最終一致性這三種:

 

n  強一致:

  當更新操做完成以後,任何多個後續進程或者線程的訪問都會返回最新的更新過的值。這種是對用戶最友好的,就是用戶上一次寫什麼,下一次就保證能讀到什麼。根據 CAP 理論,這種實現須要犧牲可用性。

 

n  弱一致:

  系統並不保證續進程或者線程的訪問都會返回最新的更新過的值。系統在數據寫入成功以後,不承諾當即能夠讀到最新寫入的值,也不會具體的承諾多久以後能夠讀到。

 

n  最終一致:

  弱一致性的特定形式。系統保證在沒有後續更新的前提下,系統最終返回上一次更新操做的值。在沒有故障發生的前提下,不一致窗口的時間主要受通訊延遲,系統負載和複製副本的個數影響。DNS 是一個典型的最終一致性系統。

 

  在分佈式系統中,同時知足「CAP定律」中的「一致性」、「可用性」和「分區容錯性」三者是幾乎不可能的。在互聯網領域的絕大多數的場景,都須要犧牲強一致性來換取系統的高可用性,系統每每只須要保證「最終一致性」,只要這個最終時間是在用戶能夠接受的範圍內便可,這時候咱們只須要用短暫的數據不一致就能夠達到咱們想要效果。

 

Ø   實例描述

好比有訂單,庫存兩個數據,一個下單過程簡化爲,加一個訂單,減一個庫存。 而訂單和庫存是獨立的服務,那怎麼保證數據一致性。

這時候咱們須要思考一下,怎麼保證兩個遠程調用「同時成功」,數據一致?

請你們先注意一點遠程調用最鬱悶的地方就是,結果有3種,成功、失敗和超時。 超時的話,成功失敗都有可能。

通常的解決方案,大多數的作法是藉助mq來作最終一致。

 

Ø   如何實現最終一致

咱們是怎麼利用Mq來達到最終一致的呢?

 

首先,拿咱們上面提到的訂單業務舉例:

在咱們進行加訂單的過程當中同時插入logA(這個過程是能夠作本地事務的)

而後能夠異步讀取logA,發mqA

B端接收mqA,同時減小庫存,B這裏須要作冪等(避免由於重複消息形成的業務錯亂)

 

那麼咱們經過上面的分析可能聯想到這樣的問題?

本地先執行事務,執行成功了就發個消息過去,消費端拿到消息執行本身的事務
好比a,b,c,a異步調用b,c若是b失敗了,或者b成功,或者b超時,那麼怎麼用mq讓他們最終一致呢?b失敗就失敗了,b成功以後給c發一個消息,b和c對a來說都是異步的,且他們都是同時進行的話,並且須要a,b,c同時成功的狀況,那麼這種狀況用mq怎麼作?

其實作法仍是參照於本地事務的概念的。

l  第一種狀況:假設a,b,c三者都正常執行,那整個業務正常結束

l  第二種狀況:假設b超時,那麼須要a給b重發消息(記得b服務要作冪等),若是出現重發失敗的話,須要看狀況,是終端服務,仍是繼續重發,甚至人爲干預(全部的規則制定都須要根據業務規則來定)

l  第三種狀況:假設a,b,c三者之中的一個失敗了,失敗的服務利用MQ給其餘的服務發送消息,其餘的服務接收消息,查詢本地事務記錄日誌,若是本地也失敗,刪除收到的消息(表示消息消費成功),若是本地成功的話,則須要調用補償接口進行補償(須要每一個服務都提供業務補償接口)。


注意事項:

  mq這裏有個坑,一般只適用於只容許第一個操做失敗的場景,也就是第一個成功以後必須保證後面的操做在業務上沒障礙,否則後面失敗了前面很差回滾,只容許系統異常的失敗,不容許業務上的失敗,一般業務上失敗一次後面基本上也不太可能成功了,要是由於網絡或宕機引發的失敗能夠經過重試解決,若是業務異常,那就只能發消息給a和c讓他們作補償了吧?一般是經過第三方進行補償,ABC提供補償接口,設計範式裏一般不容許消費下游業務失敗。


怎麼理解呢,舉個例子:
好比A給B轉帳,A先本身扣錢,而後發了個消息,B這邊若是在這以前銷戶了,那重試多少次也沒用,只能人工干預。

Ø   網易在分佈式事務採用的解決方式

網易部分業務是用MQ實現了最終一致性,目前教育產品,例如:網易雲課堂。

也有一部分業務用了TCC事務,可是TCC事務用的比較少,由於會侵染業務,開發成本比較高,若是體量不大的話直接用JPA或MQ支持事務就好。

 

網易的產品中使用分佈式事務基於技術

TCC,FMT(Framework-managed transactions),事務消息都有。

 

開源產品myth:https://gitee.com/shuaiqiyu/myth

 

4. 經常使用消息隊列(ActiveMQ、RabbitMQ、RocketMQ、Kafka)比較

特性

MQ

ActiveMQ

RabbitMQ

RocketMQ

Kafka

生產者消費者模式

支持

支持

支持

支持

發佈訂閱模式

支持

支持

支持

支持

請求迴應模式

支持

支持

不支持

不支持

Api完備性

多語言支持

支持

支持

java

支持

單機吞吐量

萬級

萬級

萬級

十萬級

消息延遲

微秒級

毫秒級

毫秒級

可用性

高(主從)

高(主從)

很是高(分佈式)

很是高(分佈式)

消息丟失

理論上不會丟失

理論上不會丟失

文檔的完備性

教高

提供快速入門

社區活躍度

商業支持

商業雲

商業雲

 

整體來講:

l  ActiveMQ 歷史悠久的開源項目,已經在不少產品中獲得應用,實現了JMS1.1規範,能夠和spring-jms輕鬆融合,實現了多種協議,不夠輕巧(源代碼比RocketMQ多),支持持久化到數據庫,對隊列數較多的狀況支持很差。

l  RabbitMQ 它比Kafka成熟,支持AMQP事務處理,在可靠性上,RabbitMQ超過Kafka,在性能方面超過ActiveMQ。

l  RocketMQ RocketMQ是阿里開源的消息中間件,目前在Apache孵化,使用純Java開發,具備高吞吐量、高可用性、適合大規模分佈式系統應用的特色。RocketMQ思路起源於Kafka,但並非簡單的複製,它對消息的可靠傳輸及事務性作了優化,目前在阿里集團被普遍應用於交易、充值、流計算、消息推送、日誌流式處理、binglog分發等場景,支撐了阿里屢次雙十一活動。 由於是阿里內部從實踐到產品的產物,所以裏面不少接口、API並非很廣泛適用。其可靠性毋庸置疑,並且與Kafka一脈相承(甚至更優),性能強勁,支持海量堆積。

  • Kafka Kafka設計的初衷就是處理日誌的,不支持AMQP事務處理,能夠看作是一個日誌系統,針對性很強,因此它並無具有一個成熟MQ應該具有的特性。Kafka的性能(吞吐量、tps)比RabbitMQ要強,若是用來作大數據量的快速處理是比RabbitMQ有優點的。
相關文章
相關標籤/搜索