提升系統性能首先考慮的是數據庫的優化,可是數據庫由於歷史緣由,橫向擴展是一件很是複雜的工程,全部咱們通常會盡可能把流量都擋在數據庫以前。html
無論是無限的橫向擴展服務器,仍是縱向阻隔到達數據庫的流量,都是這個思路。阻隔直達數據庫的流量,緩存組件和消息組件是兩大殺器。這裏就重點說說MQ的應用場景。前端
MQ:Message queue,消息隊列,就是指保存消息的一個容器。具體的定義這裏就不相似於數據庫、緩存等,用來保存數據的。固然,與數據庫、緩存等產品比較,也有本身一些特色,具體的特色後文會作詳細的介紹。java
如今經常使用的MQ組件有activeMQ、rabbitMQ、rocketMQ、zeroMQ,固然近年來火熱的kafka,從某些場景來講,也是MQ,固然kafka的功能更增強大,雖然不一樣的MQ都有本身的特色和優點,可是,無論是哪一種MQ,都有MQ自己自帶的一些特色,下面,我們就先聊聊MQ的特色。git
l 先進先出
不能先進先出,都不能說是隊列了。消息隊列的順序在入隊的時候就基本已經肯定了,通常是不需人工干預的。並且,最重要的是,數據是隻有一條數據在使用中。 這也是MQ在諸多場景被使用的緣由。web
l 發佈訂閱
發佈訂閱是一種很高效的處理方式,若是不發生阻塞,基本能夠當作是同步操做。這種處理方式能很是有效的提高服務器利用率,這樣的應用場景很是普遍。spring
l 持久化
持久化確保MQ的使用不僅是一個部分場景的輔助工具,而是讓MQ能像數據庫同樣存儲核心的數據。數據庫
l 分佈式
在如今大流量、大數據的使用場景下,只支持單體應用的服務器軟件基本是沒法使用的,支持分佈式的部署,才能被普遍使用。並且,MQ的定位就是一個高性能的中間件。緩存
消息隊列中間件是分佈式系統中重要的組件,主要解決應用解耦,異步消息,流量削鋒等問題,實現高性能,高可用,可伸縮和最終一致性架構。目前使用較多的消息隊列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ服務器
Activemq 監控網絡
Rabbitmq 監控
Kafka 監控
場景說明:用戶註冊後,須要發註冊郵件和註冊短信。傳統的作法有兩種 1.串行的方式;2.並行方式
a、串行方式:將註冊信息寫入數據庫成功後,發送註冊郵件,再發送註冊短信。以上三個任務所有完成後,返回給客戶端。
b、並行方式:將註冊信息寫入數據庫成功後,發送註冊郵件的同時,發送註冊短信。以上三個任務完成後,返回給客戶端。與串行的差異是,並行的方式能夠提升處理的時間
假設三個業務節點每一個使用50毫秒鐘,不考慮網絡等其餘開銷,則串行方式的時間是150毫秒,並行的時間多是100毫秒。
由於CPU在單位時間內處理的請求數是必定的,假設CPU1秒內吞吐量是100次。則串行方式1秒內CPU可處理的請求量是7次(1000/150)。並行方式處理的請求量是10次(1000/100)
小結:如以上案例描述,傳統的方式系統的性能(併發量,吞吐量,響應時間)會有瓶頸。如何解決這個問題呢?
引入消息隊列,將不是必須的業務邏輯,異步處理。改造後的架構以下:
按照以上約定,用戶的響應時間至關因而註冊信息寫入數據庫的時間,也就是50毫秒。註冊郵件,發送短信寫入消息隊列後,直接返回,所以寫入消息隊列的速度很快,基本能夠忽略,所以用戶的響應時間多是50毫秒。所以架構改變後,系統的吞吐量提升到每秒20 QPS。比串行提升了3倍,比並行提升了兩倍。
場景說明:用戶下單後,訂單系統須要通知庫存系統。傳統的作法是,訂單系統調用庫存系統的接口。以下圖:
傳統模式的缺點:假如庫存系統沒法訪問,則訂單減庫存將失敗,從而致使訂單失敗,訂單系統與庫存系統耦合
如何解決以上問題呢?引入應用消息隊列後的方案,以下圖:
訂單系統:用戶下單後,訂單系統完成持久化處理,將消息寫入消息隊列,返回用戶訂單下單成功
庫存系統:訂閱下單的消息,採用拉/推的方式,獲取下單信息,庫存系統根據下單信息,進行庫存操做
假如:在下單時庫存系統不能正常使用。也不影響正常下單,由於下單後,訂單系統寫入消息隊列就再也不關心其餘的後續操做了。實現訂單系統與庫存系統的應用解耦
流量削鋒也是消息隊列中的經常使用場景,通常在秒殺或團搶活動中使用普遍。
應用場景:秒殺活動,通常會由於流量過大,致使流量暴增,應用掛掉。爲解決這個問題,通常須要在應用前端加入消息隊列。
a、能夠控制活動的人數
b、能夠緩解短期內高流量壓垮應用
用戶的請求,服務器接收後,首先寫入消息隊列。假如消息隊列長度超過最大數量,則直接拋棄用戶請求或跳轉到錯誤頁面。
秒殺業務根據消息隊列中的請求信息,再作後續處理。
消息通信是指,消息隊列通常都內置了高效的通訊機制,所以也能夠用在純的消息通信。好比實現點對點消息隊列,或者聊天室等。
點對點通信:
客戶端A和客戶端B使用同一隊列,進行消息通信。
聊天室通信:
客戶端A,客戶端B,客戶端N訂閱同一主題,進行消息發佈和接收。實現相似聊天室效果。
以上實際是消息隊列的兩種消息模式,點對點或發佈訂閱模式。模型爲示意圖,供參考。
具體例子能夠參考官網:https://www.rabbitmq.com/web-stomp.html
日誌處理是指將消息隊列用在日誌處理中,好比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大同小異。
參考延時隊列
分佈式事務有強一致,弱一致,和最終一致性這三種:
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
特性 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一脈相承(甚至更優),性能強勁,支持海量堆積。