1、前言
又到了金三銀四的時候,大家都按耐不住內心的躁動,我在這里給大家分享下之前面試中遇到的一個知識點(MQ的應用場景),如有不足,歡迎大佬們指點指點,
訊息中間件應用背景
提高系統性能首先考慮的是資料庫的優化,但是資料庫因為歷史原因,橫向擴展是一件非常復雜的工程,所有我們一般會盡量把流量都擋在資料庫之前,不管是無限的橫向擴展服務器,還是縱向阻隔到達資料庫的流量,都是這個思路,阻隔直達資料庫的流量,快取組件和訊息組件是兩大殺器,
2、MQ簡介
MQ:Message queue,訊息佇列,就是指保存訊息的一個容器,
現在常用的MQ組件有activeMQ、rabbitMQ、rocketMQ,當然近年來火熱的kafka,從某些場景來說,也是MQ,不過kafka的功能更加強大,雖然不同的MQ都有自己的特點和優勢,但是,不管是哪種MQ,都有MQ本身自帶的一些特點,
常用訊息佇列比較
| 特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka |
|---|---|---|---|---|
| 生產者消費者模式 | 支持 | 支持 | 支持 | 支持 |
| 發布訂閱模式 | 支持 | 支持 | 支持 | 支持 |
| 請求回應模式 | 支持 | 支持 | 不支持 | 不支持 |
| Api完備性 | 高 | 高 | 高 | 高 |
| 多語言支持 | 支持 | 支持 | java | 支持 |
| 單機吞吐量 | 萬級 | 萬級 | 萬級 | 十萬級 |
| 訊息延遲 | 無 | 微秒級 | 毫秒級 | 毫秒級 |
| 可用性 | 高(主從) | 高(主從) | 非常高(分布式) | 非常高(分布式) |
| 訊息丟失 | 低 | 低 | 理論上不會丟失 | 理論上不會丟失 |
| 檔案的完備性 | 高 | 高 | 教高 | 高 |
| 提供快速入門 | 有 | 有 | 有 | 有 |
| 社區活躍度 | 高 | 高 | 中 | 高 |
| 商業支持 | 無 | 無 | 商業云 | 商業云 |
3、MQ特點
先進先出
先進先出,是佇列最明顯的特點,訊息佇列的順序在入隊的時候就基本已經確定了,一般是不需人工干預的,而且,最重要的是,資料是只有一條資料在使用中, 這也是MQ在諸多場景被使用的原因,
發布訂閱
發布訂閱是一種很高效的處理方式,如果不發生阻塞,基本可以當做是同步操作,這種處理方式能非常有效的提升服務器利用率,這樣的應用場景非常廣泛,
持久化
持久化確保MQ的使用不只是一個部分場景的輔助工具,而是讓MQ能像資料庫一樣存盤核心的資料,保證MQ的可靠性,
分布式
在現在大流量、大資料的使用場景下,只支持單體應用的服務器軟體基本是無法使用的,支持分布式的部署,才能被廣泛使用,而且,MQ的定位就是一個高性能的中間件,
4、應用場景
訊息佇列中間件是分布式系統中重要的組件,主要解決應用解耦,異步訊息,流量削鋒、海量日志資料同步、分布式事務等問題,實作高性能,高可用,可伸縮和最終一致性架構,
4.1 應用解耦
場景說明:用戶下單后,訂單系統需要通知庫存系統,傳統的做法是,訂單系統呼叫庫存系統的介面,
傳統模式的缺點:假如庫存系統無法訪問,則訂單減庫存將失敗,從而導致訂單失敗,訂單系統與庫存系統耦合度高,容易出現雪崩事故,
引入應用訊息佇列后的方案:
訂單系統:用戶下單后,訂單系統完成持久化處理,將訊息寫入訊息佇列,回傳用戶訂單下單成功,
庫存系統:訂閱下單的訊息,采用pull/push的方式,獲取下單資訊,庫存系統根據下單資訊,進行庫存操作,
解決問題關鍵步驟:在下單時庫存系統不能正常使用,也不影響正常下單,因為下單后,訂單系統寫入訊息佇列就不再關心其他的后續操作了,只要達成最終一致性即可,實作訂單系統與庫存系統的應用解耦,
4.2 異步訊息
場景說明:用戶注冊后,需要發注冊郵件和注冊短信,傳統的做法有兩種:串行的方式、并行方式,
-
串行方式:將注冊資訊寫入資料庫成功后,發送注冊郵件,再發送注冊短信,以上三個任務全部完成后,回傳給客戶端,
-
并行方式:將注冊資訊寫入資料庫成功后,發送注冊郵件的同時,發送注冊短信,以上三個任務完成后,回傳給客戶端,與串行的差別是,并行的方式可以提高處理的時間,
問題分析:
假設三個業務節點每個使用50毫秒鐘,不考慮網路等其他開銷,則串行方式的時間是150毫秒,并行的時間可能是100毫秒,
因為CPU在單位時間內處理的請求數是一定的,假設CPU1秒內吞吐量是100次,則串行方式1秒內CPU可處理的請求量是7次(1000/150),并行方式處理的請求量是10次(1000/100)
如以上案例描述,傳統的方式系統的性能(并發量,吞吐量,回應時間)會有瓶頸,將發送注冊郵件和注冊短信的步驟通過訊息佇列解耦:
由以上架構可知,用戶的回應時間相當于是注冊資訊寫入資料庫的時間,也就是50毫秒,注冊郵件,發送短信寫入訊息佇列后,直接回傳,因此寫入訊息佇列的速度很快,基本可以忽略,因此用戶的回應時間可能是50毫秒,因此架構改變后,系統的吞吐量提高到每秒20 QPS,比串行提高了3倍,比并行提高了兩倍,
4.3 流量削鋒
流量削鋒也是訊息佇列中的常用場景,一般在秒殺或團搶活動中使用廣泛,
場景說明:秒殺活動,一般會因為流量過大,導致流量暴增,應用或者資料庫掛掉,為解決這個問題,一般需要在應用前端加入訊息佇列,
架構如下:
加入訊息佇列的好處:
- 可以控制活動的人數
- 可以緩解短時間內高流量壓垮應用
用戶的請求,服務器接收后,首先寫入訊息佇列,假如訊息佇列長度超過最大數量,則直接拋棄用戶請求或跳轉到錯誤頁面,
秒殺業務根據訊息佇列中的請求資訊,再做后續處理,
4.4 海量日志資料同步
場景說明:在微服務體系下,專案往往是集群部署,那么就需要一個統一日志平臺來查詢各個實體的日志,但集群中的日志資訊往往都是海量資料,單一的日志采集工具不能滿足業務的需要,因此需要將訊息佇列用在日志處理中,比如Kafka的應用,解決大量日志傳輸的問題,
架構簡化如下:
架構說明:
- 日志采集客戶端,負責日志資料采集,定時寫受寫入Kafka佇列
- Kafka訊息佇列,負責日志資料的接收,存盤和轉發
- 日志處理應用:訂閱并消費kafka佇列中的日志資料
4.5 分布式事物
分布式事務又分為強一致,弱一致,和最終一致性
-
強一致:
當更新操作完成之后,任何多個后續行程或者執行緒的訪問都會回傳最新的更新過的值,這種是對用戶最友好的,就是用戶上一次寫什么,下一次就保證能讀到什么,根據 CAP 理論,這種實作需要犧牲可用性,
-
弱一致:
系統并不保證續行程或者執行緒的訪問都會回傳最新的更新過的值,系統在資料寫入成功之后,不承諾立即可以讀到最新寫入的值,也不會具體的承諾多久之后可以讀到,
-
最終一致:
弱一致性的特定形式,系統保證在沒有后續更新的前提下,系統最侄訓傳上一次更新操作的值,在沒有故障發生的前提下,不一致視窗的時間主要受通信延遲,系統負載和復制副本的個數影響,DNS 是一個典型的最終一致性系統,
在分布式系統中,同時滿足“CAP定律”中的一致性、可用性和磁區容錯性三者是幾乎不可能的,在互聯網領域的絕大多數的場景,都需要犧牲強一致性來換取系統的高可用性,系統往往只需要保證“最終一致性”,只要這個最終時間是在用戶可以接受的范圍內即可,這時候我們只需要用短暫的資料不一致就可以達到我們想要效果,
場景說明:比如有訂單,庫存兩個資料,一個下單程序簡化為,加一個訂單,減一個庫存, 而訂單和庫存是獨立的服務,那怎么保證資料一致性,
遠程呼叫最郁悶的地方就是,結果有3種,成功、失敗和超時, 超時的話,成功失敗都有可能,一般的解決方案,大多數的做法是借助mq來做最終一致,
實作最終一致:

通過上面的架構可能會想到這些問題:
本地先執行事務,執行成功了就發個訊息過去,消費端拿到訊息執行自己的事務,
比如a,b兩個服務,服務a異步呼叫服務b,如果服務b失敗了,或者成功,或者超時,那么怎么用mq讓他們最終一致呢?
參照于本地事務的概念可將該場景分為三種情況解決:
- 第一種情況:假設a,b都正常執行,那整個業務正常結束;
- 第二種情況:假設b超時,那么需要MQ給b重發訊息(b服務要做冪等),如果出現重發失敗的話,需要看情況,是中斷服務,還是繼續重發,甚至人為干預;
- 第三種情況:假設a,b之中的一個失敗了,失敗的服務利用MQ給其他的服務發送訊息,其他的服務接收訊息,查詢本地事務記錄日志,如果本地也失敗,洗掉收到的訊息(表示訊息消費成功),如果本地成功的話,則需要呼叫補償介面進行補償(需要每個服務都提供業務補償介面),
需要特別注意:
MQ這里有個坑,通常只適用于只允許第一個操作失敗的場景,也就是第一個成功之后必須保證后面的操作在業務上沒障礙,不然后面失敗了前面不好回滾,只允許系統例外的失敗,不允許業務上的失敗,通常業務上失敗一次后面基本上也不太可能成功了,要是因為網路或宕機引起的失敗可以通過重試解決,如果業務例外,那就只能發訊息給服務a讓他們做補償了吧?通常是通過第三方進行補償,各個服務需要提供補償介面,設計范式里通常不允許消費下游業務失敗,
5、總結
MQ在分布式系統開發的場景下使用的越來越多,處理的業務能力也越來越強,所以掌握MQ的使用場景是很要必要的,通過掌握MQ,即可解決大多數業務場景,也可在面試中加分,提高自己的核心競爭力,
最后,外出打工不易,希望各位兄弟找到自己心儀的作業,虎年發發發!
也希望兄弟們能關注、點贊、收藏、評論支持一波,非常感謝大家!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/435471.html
標籤:其他
上一篇:Hadoop入門(一篇就夠了)
