消息隊列學習筆記

1、市面上流行的消息隊列對比

  • ActivityMQ activityMQ是老牌的消息隊列,技術相對成熟,但吞吐量通常,目前行業趨勢漸漸用的少了,社區也相比其餘mq不夠活躍。
  • RabbitMQ,rabbitMQ是基於erlang語言開發的,國內中小企業比較流行,功能完備,特別值得一提的是管理後臺界面足夠人性化,功能豐富。而且社區活躍度較高,吞吐量還行。
  • RocketMQ 吞吐量比RabbitMq高,阿里開發,基於java語言,功能強大。
  • kafkaMQ 吞吐量比rocketMQ還要高,架構足夠輕,易於擴展,是大數據實時計算、日誌採集領域使用消息中間件的標準,但功能較少,並無"事務性""消息傳輸擔保(消息確認機制)""消息分組"等企業級特性。

總結看來,技術選型中,activityMQ通常不推薦,中小型公司用rabbitMQ(功能完備,使用較簡單,有很好的後臺界面)。 rocketMQ適合大型公司,大數據領域適合用kafka.java

2、爲何使用消息隊列

1. 解耦

相比調用而言,消息隊列有發佈訂閱模式(PUB/SUB),原來場景中的調用方只須要發送消息,不用考慮誰消費,不用手動維護調用關係,若是須要數據的系統太多維護起來是很複雜的。redis

2. 異步

消息隊列經常使用於異步場景,以此減小響應時間。算法

3. 削峯

應用消息隊列的拉模式可以很好的緩解系統在高峯期的壓力,消費者保持本身可以接收限度去拉取消息。數據庫

3、消息隊列可能致使的問題

1.可用性問題(MQ崩潰則整個系統崩潰)

以rabbitMQ爲例,rabbitMQ有三種模式:單機模式、普通集羣模式、鏡像集羣模式。服務器

單機模式

只有一個mq服務節點,沒有可用性可言網絡

普通集羣模式

一個queue不會存多個副本在每一個節點,真實數據只保存在一個節點上,但其餘節點會保存該queue的元數據元(存儲隊列指針,長度等),消費者能夠經過集羣中的任何節點獲取到數據(訪問的節點經過元數據取到實際存儲隊列的節點的數據而後返回給消費者)。但真實數據只在一個節點中,若是配置了持久化,那麼實際保存數據的節點蹦了,其餘節點不能建立一樣的隊列,因此不符合高可用性。這種模式優勢只是在於分散mq的cpu,內存壓力,提升數據存儲空間。但也會存在不少集羣內部各個節點爲了傳輸消息的網絡IO,同時可用性沒有保障。架構

鏡像集羣模式

相對普通集羣模式,全部節點同步保存隊列的真實數據。管理控制檯能夠配置一個鏡像集羣策略,指定全部節點或指定數量節點同步節點隊列。缺點是下降了系統性能,節點每次同步數據的網絡開銷大。master提供對外服務,slave節點只提供備份服務,須要注意的是,並非一個節點就是全部隊列的master節點。談論master和slave是針對隊列來談論的。併發

以Kafka爲例:異步

每一個topic有多個partion,每一個partion分佈於不一樣的節點,並且partion有副本存在於其餘節點,每一個partion有leader和follow,讀寫只存在於leader,follow會自動同步leader的數據,若是leader掛了,將從新選舉一個leader。性能

2.消息被重複消費

rabbitMq場景:消費確認機制,若是設定了手動提交,消費者收到消息忽然蹦了,致使ACK狀態碼未反饋至MQ

kafka場景:消費者會按期執行消費到的offset值提交給zookeeper用於給kafka節點從哪一個位置發送消息爲依據,可是因爲極端緣由如重啓致使沒有消費了消息可是沒有提交,kafka任會按照上一次offset的位置發送消息給消費者,這就致使了重複消費

另一種場景是:本來是想發送廣播消息到多個消費者服務中,可是一個服務實例部署了多臺,多臺重複消費了。

解決方案:設計消費方法爲冪等的。這個思路能夠是在redis存儲消費事後的一個標誌,下次消費就先判斷該標誌存不存在,存在則不進行操做。或者基於數據庫惟一鍵實現,報錯就報錯。

3.消息積壓

場景:消費端故障致使消息積壓

解決方案:

  • 快速排查好消費端故障
  • 改造原來的消費端,寫到別的服務器的隊列中,而後臨時多開幾個消費端按照原有邏輯去消費這些隊列的數據
  • 若是還設置了過時失效(通常狀況下不會這麼設置),部分數據丟了怎麼辦,手動寫程序把丟掉的數據查出來再手動發到消息隊列中去

4.消息丟失

以rabbitMQ爲例,消息丟失能夠分爲:

(1)生產者丟消息

主要是由於,寫消息的時候因爲網絡緣由沒有到rabbitmq服務器。

解決方案:

1.一是能夠開啓rabbitmq的事務模式

一次事務交互主要有如下環節:

客戶端發送給服務器Tx.Select(開啓事務模式)

服務器端返回Tx.Select-Ok(開啓事務模式ok)

推送消息

客戶端發送給事務提交Tx.Commit

服務器端返回Tx.Commit-Ok

以上就完成了事務的交互流程,若是其中任意一個環節出現問題,就會拋出IoException移除,這樣用戶就能夠攔截異常進行事務回滾,或決定要不要重複消息。可是這樣的缺點相比下面說的confirm機制,事務是同步的,吞吐量會低一點。

2.二是能夠用confirm機制

流程是:

先把channel設置爲confirm模式,發送消息,發完就無論了

生產者提供一個接口,用於實現成功/失敗回調後的方法

接收失敗的話能夠直接再重發一次

(2)MQ丟消息

MQ丟消息的狀況多是:rabbitmq接收到消息,在消費者消費以前掛掉了。

解決方案(兩步):

1.設置隊列元數據持久化,設爲durable;

2.生產者設置消息持久化,delivery_mode=2。

另外:

若設置了持久化且開啓了confirm,將在持久化以後纔回調生產者。

若持久化過程當中宕機,仍是會丟失數據,除非結合confirm機制。

(3)消費者丟消息

消費者打開了autoAck機制(消費者收到消息自動響應mq消費到了數據),若是正在消費時宕機了,就丟消息了。

解決方案:關閉ack改成手動ack(若是要在乎最終數據一致性,最好是在數據庫本地事務以後手動發送ack),若是mq沒收到ack,mq將把消息發送給其餘消費者。

以kafka爲例:

(1) 生產者丟消息

若是設置了ack=all,必定不會丟,同時設置了retries=max,則會無限重試

(2) MQ丟消息

場景:有可能內存還沒同步到日誌文件或者沒有同步到Follower可是本身down掉了。

參考解決方案:

topic設置repication.factor參數,必須大於1,要求每一個Partition必須至少有2個副本。

kafka服務端設置min.insync.replicas大於1,要求一個leader至少須要感知到一個follower還跟本身保持聯繫。

生產者設置acks=all,要求每條數據必須是寫入全部replica以後,才能認爲是寫成功了。

在producer端設置retries=max(很大的值),要求一旦寫入失敗,就無線重試。

(3)消費者丟消息

消費者自動提交了offset,可是處理業務中卻異常宕機。取消offset,改成手動提交。

3.消息順序變了

消息順序有時候會影響業務,好比先修改後刪除的兩條消息,不按順序來消費就會出錯。

從rabbitMQ上來講,須要保證順序的隊列避免多個消費者同時消費(避免工做隊列,只有一個消費者去消費)。

從kafka上來講,多個消費者同時消費的消息指定到了不一樣的key分發到了不一樣的partion致使不一樣消費者同時消費。由於kafka的一個partion只容許一個消費者,因此只須要把保證順序的消息發送到同一個pation,讓其被同一個消費者消費就好了。

另外也有多是消費者本身併發的去消費信息形成的,針對這種狀況,咱們能夠經過對消息的惟一標識進行hash算法分派到不一樣的內存隊列,而後線程只取其中一個內存隊列的消息進行消費來控制相關聯的消息順序不會亂。

4.一致性問題

通常使用消息隊列只要配置對,使用消息確認機制,是可以保證一條消息的可靠投遞的,問題主要在於若是一個事務發起方發送了多條消息,那麼多條消息的最終一致性怎麼解決,針對此rocketMq有成熟的解決方案,但其餘消息隊列沒有,咱們能夠本身寫一箇中間的消息服務維護這種確認機制(上篇博文有)。

相關文章
相關標籤/搜索