什么是MQ?
【1】MQ:MessageQueue,訊息佇列, 佇列,是一種FIFO 先進先出的資料結構,訊息由生產者發送到MQ進行排隊,然后按原來的順序交由訊息的消費者進行處理,QQ和微信就是典型的MQ,
為什么要用MQ(MQ的優點)?
MQ的作用主要有以下三個方面:
【1】異步
例子:快遞員發快遞,直接到客戶家效率會很低,引入菜鳥驛站后,快遞員只需要把快遞放到菜鳥驛站,就可以繼續發其他快遞去了,客戶再按自己的時間安排去菜鳥驛站取快遞,
作用:異步能提高系統的回應速度、吞吐量,
【2】解耦
例子:《Thinking in JAVA》很經典,但是都是英文,我們看不懂,所以需要編輯社,將文章翻譯成其他語言,這樣就可以完成英語與其他語言的交流,
作用:
1、服務之間進行解耦,才可以減少服務之間的影響,提高系統整體的穩定性以及可擴展性,
2、另外,解耦后可以實作資料分發,生產者發送一個訊息后,可以由一個或者多個消費者進行消費,并且消費者的增加或者減少對生產者沒有影響,
【3】削峰
例子:長江每年都會漲水,但是下游出水口的速度是基本穩定的,所以會漲水,引入三峽大壩后,可以把水儲存起來,下游慢慢排水,
作用:以穩定的系統資源應對突發的流量沖擊,
MQ的缺點
【1】系統可用性降低
系統引入的外部依賴增多,系統的穩定性就會變差,一旦MQ宕機,對業務會產生影響,這就需要考慮如何保證MQ的高可用,
【2】系統復雜度提高
引入MQ后系統的復雜度會大大提高,以前服務之間可以進行同步的服務呼叫,引入MQ后,會變為異步呼叫,資料的鏈路就會變得更復雜,并且還會帶來其他一些問題,比如:如何保證消費不會丟失?不會被重復呼叫?怎么保證訊息的順序性等問題,
【3】訊息一致性問題
A系統處理完業務,通過MQ發送訊息給B、C系統進行后續的業務處理,如果B系統處理成功,C系統處理失敗怎么辦?這就需要考慮如何保證訊息資料處理的一致性,
常用的MQ產品
【1】Kafka、RabbitMQ和RocketMQ,我們對這三個產品做下簡單的比較,重點需要理解他們的適用場景,
【2】圖示:

【3】分別分析三種訊息中間件
1.RabbitMQ:訊息可靠性很高,功能非常全面,很多高級功能都是從這里衍生出來的,如死信佇列,延遲佇列,缺點在于吞吐量很低,訊息積累會影響消費的性能,而且erlang的語言使用的比較少,定制比較難,適用于公司內部系統的請求扭轉的流程,
2.Kafka:行業的老大哥,基本上是大資料場景必用的組件之一,吞吐量不可挑戰,集群性能很好,之前是依賴zookeeper搭建集群,但是新版本會逐漸拋棄zookeeper,但是會存在丟消失的可能,而且功能單一,很多高級功能都沒有,如死信佇列,最早就是用來做日志分析的,
3.RocketMQ:最開始是借鑒Kafka,后面逐步優化,吞吐量基本和Kafka是一個量級的,功能也很全面,如RabbitMQ有的都有,還有其他沒有的事務功能,缺點是開源版不如云上商業版,如延遲佇列,開源會有固定的限制,
MQ使用中的常見問題
【1】如何保證訊息不丟失?
1)分析哪些環節會有丟訊息的可能
(1)圖示

(2)分析
1.其中,1,2,4三個場景都是跨網路的,而跨網路就肯定會有丟訊息的可能,
2.而3這個環節,通常MQ存盤時都會先寫入作業系統的快取page cache中,然后再由作業系統異步的將訊息寫入硬碟,這個中間有個時間差,就可能會造成訊息丟失,如果服務掛了,快取中還沒有來得及寫入硬碟的訊息就會丟失,這也是任何用戶態的應用程式無法避免的,
2)分析怎么處理的
1.為保證訊息不丟失,發送端的ACK應答必須是多個節點寫入的應答 兼 采用多次重試的方式(預防網路抖動),其次訊息中間件內部持久化,消費端是消費后手動應答,
2.在發送端還應該:區分業務的關鍵性,如果訊息不影響主體業務(如,訊息通知要做的事情可以延遲很久,但因某些緣故,訊息發不出去),這時候采用將訊息落盤,然后呼叫定時任務的形式,延時檢查發送,
3.在消費端還應該:對消費失敗的訊息進行次數檢測,如果多次失敗(有可能引數例外,有可能流程出了問題),應該落盤(避免訊息堆積),告知程式員處理,
【2】如何保證訊息冪等性?
1)分析哪些環節會造成訊息重復消費
1.MQ的自動重試功能:如網路抖動時,生產者發送得不到MQ的回應嘗試多次發送;消費者做完任務,回傳給MQ的應答丟失,導致MQ發給了另一個消費者去消費訊息,
2.代碼BUG導致訊息多次發送,
2)分析怎么處理的
1.首先在MQ上我們是不能保證訊息的冪等性的,所以我們只能在業務中處理,
2.處理冪等問題的關鍵是要給每個訊息一個唯一的標識(但這個不能是MQ給我們的訊息ID,因為它依舊解決不了生產者發送多次的問題)
3.需要我們自行構建分布式唯一ID(如雪花演算法),能夠添加一個具有業務意義的資料作為唯一鍵會更好,這樣能更好的防止重復消費問題對業務的影響,比如,針對訂單訊息,那就用訂單ID來做唯一鍵,
4.如訂單ID來做唯一鍵,就算真的出現了很不幸的兩個消費者同時消費兩條重復的資料,那么在進行MYSQL寫入的時候,事務處理與唯一鍵索引,將是兜底保證業務執行冪等性的關鍵,
5.當然,采用redis的Setnx(要設定超時時間)作為CAS鎖保證只有一個執行緒執行業務也是可以的,成功后還可以設定標記值來標記該業務已經做完,等下次重復的訊息過來時候,進行redis檢驗的時候就會自動丟棄這些重復的訊息,【這里面需要衡量的是業務的處理速度,與占用redis的記憶體空間,雖然有過期時間,但是在這段時間內這些資料依舊會占用空間,如果處理速度很快,則占用的空間越多】
【3】如何保證訊息的順序?
1)原因:某些場景下,需要保證訊息的消費順序,例如一個下單程序,需要先完成扣款,然后扣減庫存,然后通知快遞發貨,這個順序不能亂,如果每個步驟都通過訊息進行異步通知的話,這一組訊息就必須保證他們的消費順序是一致的
2)分析該怎么處理(基于MQ無法保證,那么更多是在業務層面實作)
方案一:為保證訊息的有序性,采用用同步發送的模式去發訊息,然后訊息發往同一個佇列里面,然后采用一個消費者去進行消費,
方案二:為保證高性能,采用用異步發送的模式去發訊息,然后訊息發往同一個佇列里面,然后采用一個消費者去進行消費,消費者端接收后,因為可能訊息群是亂序的(異步發送模式),所以構建記憶體佇列(優先級佇列),將訊息排序消費(每個記憶體佇列只允許一個執行緒消費,可拓展為多個記憶體佇列多個執行緒)
針對這種,容易出現訊息堆積的情況,可擴展為多個佇列,每個佇列都有唯一的一個消費者,在發送端建立訊息組ID,根據組ID進行hash決定這一組訊息分配至哪個佇列里面,但是又容易出現資料傾斜的問題,則可以考慮構建hash環與增加虛擬節點的想法,將資料更加均勻的分布,
【4】資料堆積如何處理?
1)線上有時因為發送方發送訊息速度過快,或者消費方處理訊息過慢,可能會導致MQ積壓大量未消費訊息,此種情況如果積壓了上百萬未消費訊息需要緊急處理,可以修改消費端程式,讓其將收到的訊息快速轉發到其他佇列,然后再啟動多個消費者同時消費,
2)由于訊息資料格式變動或消費者程式有bug,導致消費者一直消費不成功,也可能導致MQ積壓大量未消費訊息,此種情況可以將這些消費不成功的訊息轉發到其它佇列里去(類似死信佇列),后面再慢慢分析死信佇列里的訊息處理問題,
MQ的自動重試功能
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/517509.html
標籤:Java
