消息隊列MQ

本文參考 https://github.com/doocs/advanced-java/blob/master/docs/high-concurrency/why-mq.md

https://www.yuque.com/liangxinjiang/powiyk/akyt35

一. 消息隊列的組成

1. Broker 消息服務器,作爲server提供消息核心服務

2. Producer 消息生產者,業務的發起方,負責生產消息傳輸給broker

3. Consumer 消息消費者,業務的處理方,負責從broker獲取消息並進行業務邏輯處理

4. Topic 隊列,PTP(Point-to-Point點對點)模式下,特定生產者向特定queue發送消息,消費者訂閱特定的Queue完成指定消息的接收

5. Message 消息體,根據不同通信協議定義的固定格式進行編碼的數據包,來封裝業務數據,實現消息的傳輸

二:模式分類

1. 點對點 Point-to-Point     使用queue作爲通信載體

 生產者發送消息到queue中,然後消費者從queue中拉取並進行消費。消息被消費後,queue中不再存儲,所以消費者不可能消費到已經被消費的消息,Queue支持存在多個消費者,但是對一個消息而言,只有一個消費者可以消費。

2. 發佈/訂閱模式

Pub/Sub 發佈訂閱(廣播):使用topic作爲通信載體

消息生產者將消息發佈到topic中,同時有多個消息消費者(訂閱)消費該消息。和點對點方式不同,發佈到topic的消息會被所有訂閱者消費。 

queue實現了負載均衡,將producer生產的消息發送到消息隊列中,由多個消費者進行消費,但一個消費者只能被一個消費者接受,當沒有消費者可用時,這個消息會被保存知道有一個可用的消費者。

topic 實現了發佈和訂閱,當你發佈一個消息,所有訂閱這個topic的服務都能得到這個消息,所以從1到N個訂閱者都能得到一個消息的拷貝。

二. 爲什麼使用消息隊列

根據具體的業務場景來說。解耦,冗餘,異步通信,擴展性,過載保護,可恢復性,順序保證,緩衝,數據流處理,比較重要的有:解耦、異步、削峯(過載保護)

1. 解耦

場景1:ABCD四個系統(模塊),A系統發送數據到BCD三個系統,系統之間通過接口調用發送,這時候C系統,不需要A系統發送數據了,而新增了一個E系統需要A系統發送數據。

這個場景下。A系統與其他系統嚴重耦合,A系統發送數據時,要時刻考慮其他系統是否正常、要不要進行重發、發送的數據是否要保存起來避免出錯。而這些問題在使用MQ之後都可以進行更好的處理。

A系統發送一條數據,發送到MQ中去,哪個系統需要就自己去MQ裏消費,如果添加新系統,新系統也去MQ消費即可,哪個系統不需要數據了,就取消該系統對MQ消息的消費。通過使用一個MQ,Pub/Sub發佈訂閱消息模型,A系統就不需要考慮給誰發送,失敗超時等問題,與其他系統徹底解耦。

2. 異步

場景2:A 系統接受一個請求,需要在自己本地寫庫,還需要BCD三個系統寫庫,自己本地寫庫需要3ms,BCD三個系統寫庫分別需要300ms,450ms,200ms。最終請求總延時是 3 + 300 + 450 + 200 = 953ms,接近1s,這會導致用戶體驗很差,無法接受。

如果使用MQ,那麼A系統連續發送3條消息到MQ隊列中,假如耗時5ms,A系統從接受一個請求到返回響應給用戶,總時長是 3+5=8ms,用戶幾乎感知不到,這就不會影響到用戶體驗。  

3. 削峯 過載保護

場景3:A系統每天1:00~2:00,每秒併發請求數量就會暴增到5k+條,其他時間正常,每秒併發請求數量就50~100條,A系統是直接基於MySQL的,大量的請求涌入MySQL。每秒鐘需要對MySQL執行約5k+條SQL,但一般的MySQL無法實現每秒5k+的操作,如果每秒請求數量到5k的話,可能會直接把MySQL給乾死,導致系統game over。而且高峯期一過,每秒鐘的請求數量就降下來了,對整個系統無任何壓力。

使用MQ,每秒5k個請求寫入MQ,A系統每秒鐘最多處理2k個請求,因爲MySQL的性能限制它每秒鐘只能處理這麼多,那麼A系統從MQ中拉取請求,每秒鐘拉取2k個請求進行處理,不超過自己每秒能處理的最大請求數量就OK,這樣下來,即便是高峯期的時候,A系統也不會掛掉,這樣的結果是MQ中會積壓大量的請求,這個短暫的高峯期是沒問題的,因爲高峯期過了之後,每秒鐘就50~100個請求進入MQ,但A系統依舊會按照每秒2k個請求的速度處理,所以高峯期過後,MQ中積壓的消息A系統會快速的給解決掉。

可恢復性:系統的一部分組件失效時,不會影響到整個系統。消息隊列降低了進程間的耦合度,所以即使一個處理消息的進程掛掉,加入隊列中的消息仍然可以在系統恢復後被處理。

數據流處理:分佈式系統產生的海量數據流,如:業務日誌、監控數據、用戶行爲等,針對這些數據流進行實時或批量數據採集,然後進行大數據分析是當前互聯網的必備集數,通過消息隊列完成此類數據收集是最好的選擇。

三. 消息隊列有什麼優缺點 

消息隊列MQ是一種非常複雜得架構,引入它有很多好處,但是也得針對它帶來的壞處做各種額外的處理來進行規避,這樣就使得系統複雜度直線上升,但有些情況下你還不得不用。

優點:特殊場景下的應用,解耦、異步(提高系統響應時間)、削峯、爲大數據處理框架提供服務

缺點:

1. 系統可用性降低

系統引入的外部依賴越多,越容易出現問題,加入MQ後,MQ一旦出現問題,整套系統全部崩潰,只能gg,這時就要考慮MQ的高可用。

2. 系統複雜度提高

加入MQ後,你得考慮如何保證消息不被重複消費、如何處理消息丟失、如何保證消息傳遞的順序性等一堆問題,頭大。

3. 一致性問題(****)

A系統處理完了直接返回成功了,客戶以爲這個請求就成功了,但問題是,你A系統處理完了,還需要BCD系統進行寫庫操作,然後BC系統寫庫成功,D系統寫庫失敗了,咋整,數據不一致了。

四. MQ高可用:

RabbitMQ 是基於主從(非分佈式)做高可用性的。有三種模式:單機模式,普通集羣模式,鏡像集羣模式。

單機模式模式:自己玩玩兒,生產環境沒人會用的。

普通集羣模式:在多臺機器上啓動多個RabbitMQ實例,每個機器啓動一個。你創建的queue,只會放在一個RabbitMQ實例上,但是每個實例都同步queue的元數據(元數據:queue的一些配置信息,通過元數據,可以找到queue所在實例)。你消費的時候,實際上如果連接到了另外一個實例,那麼那個實例會從queue所在實例上拉取數據過來。這種方式很麻煩,也沒做到所謂的分佈式,就是個普通集羣。這導致你要麼消費者每次隨機連接一個實例然後拉取數據,要麼固定連接那個queue所在實例消費數據前者有數據拉取的開銷,後者導致單例性能瓶頸。而且如果那個放queue的實例宕機了,會導致接下來其他實例就無法從那個實例拉取,如果你開啓了消息持久化,讓RabbitMQ落地存儲消息的話,消息不一定會丟,得等到這個實例恢復了,纔可以繼續從這個實例拉取數據。所以這個方案沒有高可用性,主要是用來提高吞吐量的,讓集羣中多個節點來服務某個queue的讀寫操作