其實mq 的場景有很多,但是比較核心的有3個:非同步處理、削峰填谷、應用解耦。
非同步處理:
使用者註冊後,需要傳送註冊郵件和註冊簡訊。假設註冊資訊加入資料庫需要30ms,傳送註冊郵件需要40ms。傳送註冊簡訊需要50ms。總共需要的時間就是30+40+50=120ms。可能使用者會感覺太慢了
但是一旦加入 mq 之後,系統只需要將客戶資訊放入資料庫就可以直接返回給使用者註冊成功的資訊。然後用mq非同步處理簡訊和郵箱的驗證。而簡訊和郵箱的驗證因為網路問題。使用者是可以接受一定時間的延遲的。那麼算下來使用者感知到這個介面的耗時僅僅是30ms,節約了2/3的時間。使用者體驗那是倍兒爽。
削峰填谷:
秒殺在我們的日常中相當常見。在秒殺的過程中,系統都發生了什麼呢?
假設我們的資料庫每秒能處理最大的資料量是100條。但是在活動秒殺的時候,資料量激增到每秒一萬條。這樣一來伺服器不堪重負就會gg掉。所以就要考慮優化我們的架構,而mq正是解決辦法之一!
具體辦法就是將秒殺的信心和資料放入訊息佇列。系統按照之前的處理速度來從容的處理這些資料。而不是直接將全部資料湧入系統,避免宕機問題的發生。
降低系統之間的耦合度:
系統a下面有兩個子系統,乙個 b 乙個 c。這兩個子系統都高度依賴於系統a,也就是系統a有所改動的話,那麼系統b、c也要做相應的改動。萬一有一百個子系統呢?想想都累啊。。
所以這時候就要引入mq作為中介軟體了。這樣一來子系統只依賴訊息而不再依賴於具體的介面。只要 a 系統把訊息扔給 mq 就不用管了。這樣即使系統 a 增加或者修改服務的時候。都不會影響其子系統。
系統可用性降低:
你想啊,本來其他系統只要執行好好的,那你的系統就是正常的。現在你非要加個訊息佇列進去,那訊息佇列掛了,你的系統不就gg了。因此,系統可用性降低
系統複雜性增加:
要多考慮很多方面的問題,比如一致性問題、如何保證訊息不被重複消費,如何保證保證訊息可靠傳輸。因此,需要考慮的東西更多,系統複雜性增大。
資料一致性問題:
本來好好的,a 系統呼叫bc子系統介面,如果子bc系統出錯了,會丟擲異常,返回給a系統,讓a系統知道,這樣的話就可以做回滾操作了,但是使用了 mq 之後,a 系統傳送完訊息就完事了,就認為成功了。但是剛好 c 在系統寫資料庫的時候失敗了,但是 a 認為 c 已經成功了?這樣一來資料就不一致了。
訊息生產失敗:
一般來說,從生產者到mq中介軟體是通過網路呼叫的,是網路呼叫就有可能存在失敗。訊息佇列通常使用確認機制,來保證訊息可靠傳遞:當你**呼叫傳送訊息的方法,訊息佇列的客戶端會把訊息傳送到broker,broker接受到訊息會返回客戶端乙個確認。只要producer收到了broker的確認響應,就可以保證訊息在生產階段不會丟失。有些訊息佇列在長時間沒收到傳送的確認響應後,會自動重試,如果重試再失敗,就會立刻返回值或者異常方式返回給客戶端。所以在編寫傳送訊息的**,需要正確處理訊息傳送返回值或者異常,保證這個階段訊息不丟失。
mq處理儲存失敗:
訊息到達訊息中介軟體之後,通常是會被儲存起來的,只有被寫入到磁碟中,訊息才是真正地被儲存,不會丟失。但是,大部分mq中介軟體並不是收到訊息就立馬寫入磁碟的,只是由於磁碟的寫入速度相對於記憶體慢很多,所以,像kafka這樣的訊息系統,是會把訊息寫到緩衝區中,非同步寫入磁碟,如果機器在中途突然斷電,是有可能會丟失訊息的。為了解決這個問題,大部分的mq都是採用分布式部署,訊息會在多台機器上寫入快取中成功才會返回給業務方成功,由於多台機器同時斷電的可能性較低,我們可以認為這是比較低成本又可靠的方案。
消費者處理失敗:
一般的mq都有mq重試機制,如果處理失敗,就會嘗試重複消費這個mq。這個帶來的問題就是,mq可能已經成功消費了,但是在通知mq中介軟體的時候失敗了,這個時候帶來的結果就是訊息重複消費。同理,在生產者重試的時候,也會遇到訊息重複消費的問題。這個時候,就要求我們盡量把介面設計得有冪等性,這個時候即便是重複消費,也不用擔心什麼問題了
4.怎麼保證 mq 的高可用性?
rabbitmq 是比較有代表性的,因為是基於主從做高可用性的,我們就以他為例子講解第一種 mq 的高可用性怎麼實現。
rabbitmq有三種模式:單機模式、普通集群模式、映象集群模式。
單機模式就是 demo 級別的,就是說只有一台機器部署了乙個 rabbitmq 程式。這個會存在單點問題,宕機就玩完了,沒什麼高可用性可言。一般就是你本地啟動了玩玩兒的,沒人生產用單機模式。
普通集群模式,意思是多台機器啟動多個rabbitmq例項,每個機器啟動乙個。你建立的queue,只會放在乙個rabbitmq例項上。但是每個例項會同步queue的元資料(可以理解queue的配置資訊)。即使你消費的時候連線到了另乙個例項,那麼那個例項會從queue所在例項上拉取資料過來
其實並沒有做到所謂的分布式,還是普通集群。因為會導致你要麼消費者每次隨機連線乙個例項然後拉取資料,要麼固定連線那個queue所在例項消費資料。前者有資料拉取的開銷,後者會導致單例項效能瓶頸。
如果放queue的例項宕機了,則會導致其他例項都沒有辦法進行拉取。如果你開啟了訊息持久化,讓rabbitmq落地儲存訊息的話,訊息不一定會丟。得等這個例項恢復了,然後才可以繼續從這個queue拉取資料。
這個方案主要是提高吞吐量。也就是說讓多個節點來服務某個queue的讀寫操作。
映象集群模式才是所謂的rabbitmq的高可用模式,跟普通集群模式不一樣的是,你建立的queue,無論元資料還是queue裡的訊息都會存在於多個例項上,然後每次你寫訊息到queue的時候,都會自動把訊息到多個例項的queue裡進行訊息同步。
這樣的話,好處在於你任何乙個機器宕機了,沒事兒,別的機器都可以用。
壞處在於這個效能開銷也太大了吧,訊息同步所有機器,導致網路頻寬壓力和消耗很重!
這麼玩兒,就沒有擴充套件性可言了,如果某個queue負載很重,你加機器,新增的機器也包含了這個queue的所有資料,並沒有辦法線性擴充套件你的queue那麼怎麼開啟這個映象集群模式呢?
這裡簡單說一下,避免面試人家問你你不知道,其實很簡單rabbitmq有很好的管理控制台,就是在後台新增乙個策略,這個策略是映象集群模式的策略,指定的時候可以要求資料同步到所有節點的,也可以要求就同步到指定數量的節點,然後你再次建立queue的時候,應用這個策略,就會自動將資料同步到其他的節點上去了。
如何設計乙個訊息佇列
其實聊到這個問題,一般面試官要考察兩塊 你有沒有對某乙個訊息佇列做過較為深入的原理的了解,或者從整體了解把握住乙個訊息佇列的架構原理。看看你的設計能力,給你乙個常見的系統,就是訊息佇列系統,看看你能不能從全域性把握一下整體架構設計,給出一些關鍵點出來。說實話,問類似問題的時候,大部分人基本都會蒙,因...
用 Promise 實現乙個訊息佇列
在此篇部落格中,我們的需求如下 有乙個訊息排程器去操作傳送來訊息 但處理訊息花費的事件是不確定的,有多有少 訊息是不斷傳送過來的 這個時候就會出現一種情況 前一條訊息還未執行結束,後一條訊息就被傳送過來了 如果這個時候要求後一條訊息必須在前一條執行完才開始執行,該如何實現?sync function...
乙個應用程式產生乙個訊息佇列嗎
一般來講,乙個應用程式對應唯一的乙個程序號,該程序號對應的資源 建立的時間 記憶體等,都是一一對應的,該程序產生的訊息也是唯一的,不會跟其他應用程式衝突。應用程式需要跟核心 或稱系統 進行互動,那麼就要傳遞訊息,就有訊息管理機制。應用程式產生的訊息,會加入系統的訊息佇列當中,根據優先順序管理 排程等...