RabbitMQ 常見問題

来源:https://www.cnblogs.com/taojietaoge/archive/2022/11/17/16895578.html
-Advertisement-
Play Games

RabbitMQ 常見問題 昔我往矣,楊柳依依。今我來思,雨雪霏霏。 1、什麼是RabbitMQ? RabbitMQ是一款開源的、Erlang編寫的消息中間件;最大的特點就是消費並不需要確保提供方存在,實現了服務之間的高度解耦,可以用它來:解耦、非同步、削峰。 2、MQ的優點 非同步處理 - 相比於傳統 ...


RabbitMQ 常見問題       昔我往矣,楊柳依依。今我來思,雨雪霏霏。

1、什麼是RabbitMQ? 

RabbitMQ是一款開源的、Erlang編寫的消息中間件;最大的特點就是消費並不需要確保提供方存在,實現了服務之間的高度解耦,可以用它來:解耦、非同步、削峰。 

2、MQ的優點

  • 非同步處理 - 相比於傳統的串列、並行方式,提高了系統吞吐量。
  • 應用解耦 - 系統間通過消息通信,不用關心其他系統的處理。
  • 流量削鋒 - 可以通過消息隊列長度控制請求量;可以緩解短時間內的高併發請求。
  • 日誌處理 - 解決大量日誌傳輸。
  • 消息通訊 - 消息隊列一般都內置了高效的通信機制,因此也可以用在純的消息通訊。比如實現點對點消息隊列,或者聊天室等。

3、消息隊列有什麼缺點

1. 系統可用性降低 本來系統運行好好的,現在你非要加入個消息隊列進去,那消息隊列掛了,你的系統不是呵呵了。因此,系統可用性會降低; 2. 系統複雜度提高 加入了消息隊列,要多考慮很多方面的問題,比如:一致性問題、如何保證消息不被重覆消費、如何保證消息可靠性傳輸等。因此,需要考慮的東西更多,複雜性增大。 3. 一致性問題 A 系統處理完了直接返回成功了,人都以為你這個請求就成功了;但是問題是,要是 BCD 三個系統那裡,BD 兩個系統寫庫成功了,結果 C 系統寫庫失敗了,咋整?你這數據就不一致了。

4、你們公司生產環境用的是什麼消息中間件?

  • 比如用的是RabbitMQ,然後可以初步給一些你對不同MQ中間件技術的選型分析。
  • 舉個例子:比如說ActiveMQ是老牌的消息中間件,國內很多公司過去運用的還是非常廣泛的,功能很強大。但是問題在於沒法確認ActiveMQ可以支撐互聯網公司的高併發、高負載以及高吞吐的複雜場景,在國內互聯網公司落地較少。而且使用較多的是一些傳統企業,用ActiveMQ做非同步調用和系統解耦。
  • 然後可以說說RabbitMQ,他的好處在於可以支撐高併發、高吞吐、性能很高,同時有非常完善便捷的後臺管理界面可以使用。另外,他還支持集群化、高可用部署架構、消息高可靠支持,功能較為完善。而且經過調研,國內各大互聯網公司落地大規模RabbitMQ集群支撐自身業務的case較多,國內各種中小型互聯網公司使用RabbitMQ的實踐也比較多。除此之外,RabbitMQ的開源社區很活躍,較高頻率的迭代版本,來修複發現的bug以及進行各種優化,因此綜合考慮過後,公司採取了RabbitMQ。但是RabbitMQ也有一點缺陷,就是他自身是基於erlang語言開發的,所以導致較為難以分析裡面的源碼,也較難進行深層次的源碼定製和改造,畢竟需要較為扎實的erlang語言功底才可以。
  • 然後可以聊聊RocketMQ,是阿裡開源的,經過阿裡的生產環境的超高併發、高吞吐的考驗,性能卓越,同時還支持分散式事務等特殊場景。而且RocketMQ是基於Java語言開發的,適合深入閱讀源碼,有需要可以站在源碼層面解決線上生產問題,包括源碼的二次開發和改造。
  • 另外就是Kafka,Kafka提供的消息中間件的功能明顯較少一些,相對上述幾款MQ中間件要少很多。但是Kafka的優勢在於專為超高吞吐量的實時日誌採集、實時數據同步、實時數據計算等場景來設計。因此Kafka在大數據領域中配合實時計算技術(比如Spark Streaming、Storm、Flink)使用的較多。但是在傳統的MQ中間件使用場景中較少採用。

5、MQ 有哪些常見問題?如何解決這些問題?

MQ 的常見問題有:
  • 消息的順序問題
  • 消息的重覆問題

消息的順序問題

消息有序指的是可以按照消息的發送順序來消費。 假如生產者產生了 2 條消息:M1、M2,假定 M1 發送到 S1,M2 發送到 S2,如何保證 M1 先於 M2 被消費? 解決方案: 1. 保證生產者 - MQServer - 消費者是一對一對一的關係

缺陷:
  • 並行度就會成為消息系統的瓶頸(吞吐量不夠)
  • 更多的異常處理,比如:只要消費端出現問題,就會導致整個處理流程阻塞,我們不得不花費更多的精力來解決阻塞的問題。
  • 不關註亂序的應用實際大量存在
  • 隊列無序並不意味著消息無序,所以從業務層面來保證消息的順序而不僅僅是依賴於消息系統,是一種更合理的方式。

消息的重覆問題

  • 造成消息重覆的根本原因是:網路不可達。
  • 所以解決這個問題的辦法就是繞過這個問題。那麼問題就變成了:如果消費端收到兩條一樣的消息,應該怎樣處理?
  • 消費端處理消息的業務邏輯保持冪等性。只要保持冪等性,不管來多少條重覆消息,最後處理的結果都一樣。保證每條消息都有唯一編號且保證消息處理成功與去重表的日誌同時出現。利用一張日誌表來記錄已經處理成功的消息的 ID,如果新到的消息 ID 已經在日誌表中,那麼就不再處理這條消息。

6、rabbitmq 的使用場景

(1)服務間非同步通信 (2)順序消費 (3)定時任務 (4)請求削峰 

7、RabbitMQ基本概念

  • Broker: 簡單來說就是消息隊列伺服器實體
  • Exchange: 消息交換機,它指定消息按什麼規則,路由到哪個隊列
  • Queue: 消息隊列載體,每個消息都會被投入到一個或多個隊列
  • Binding: 綁定,它的作用就是把exchange和queue按照路由規則綁定起來
  • Routing Key: 路由關鍵字,exchange根據這個關鍵字進行消息投遞
  • VHost: vhost 可以理解為虛擬 broker ,即 mini-RabbitMQ server。其內部均含有獨立的queue、exchange 和 binding 等,但最最重要的是,其擁有獨立的許可權系統,可以做到 vhost 範圍的用戶控制。當然,從 RabbitMQ 的全局角度,vhost 可以作為不同許可權隔離的手段(一個典型的例子就是不同的應用可以跑在不同的 vhost 中)。
  • Producer: 消息生產者,就是投遞消息的程式
  • Consumer: 消息消費者,就是接受消息的程式
  • Channel: 消息通道,在客戶端的每個連接里,可建立多個channel,每個channel代表一個會話任務
  • 由Exchange、Queue、RoutingKey三個才能決定一個從Exchange到Queue的唯一的線路。 

8、RabbitMQ的工作模式 

simple模式(即最簡單的收發模式)

1. 消息生產者產生消息,將消息放入隊列。 2. 消息的消費者(consumer) 監聽 消息隊列,如果隊列中有消息,就消費掉,消息被拿走後,自動從隊列中刪除(隱患:消息可能沒有被消費者正確處理,就已經從隊列中消失了,造成消息的丟失,這裡可以設置成手動的ack,但如果設置成手動ack,處理完後要及時發送ack消息給隊列,否則會造成記憶體溢出)。

work工作模式(資源的競爭)  

消息產生者將消息放入隊列,消費者可以有多個,消費者1、消費者2 同時監聽同一個隊列,消息被消費。 C1、C2共同爭搶當前的消息隊列內容,誰先拿到誰負責消費消息(隱患:高併發情況下,預設會產生某一個消息被多個消費者共同使用,可以設置一個開關(syncronize) 保證一條消息只能被一個消費者使用)。 

publish/subscribe發佈訂閱(共用資源) 

  • 每個消費者監聽自己的隊列;
  • 生產者將消息發給broker,由交換機將消息轉發到綁定此交換機的每個隊列,每個綁定交換機的隊列都將接收到消息。

routing路由模式 

  • 消息生產者將消息發送給交換機按照路由判斷,路由是字元串(info) 當前產生的消息攜帶路由字元(對象的方法),交換機根據路由的key,只能匹配上路由key對應的消息隊列,對應的消費者才能消費消息;
  • 根據業務功能定義路由字元串
  • 從系統的代碼邏輯中獲取對應的功能字元串,將消息任務扔到對應的隊列中。
  • 業務場景:error 通知;EXCEPTION;錯誤通知的功能;傳統意義的錯誤通知;客戶通知;利用key路由,可以將程式中的錯誤封裝成消息傳入到消息隊列中,開發者可以自定義消費者,實時接收錯誤;。

topic 主題模式(路由模式的一種) 

1. 星號井號代表通配符 2. 星號代表多個單詞,井號代表一個單詞 3. 路由功能添加模糊匹配,routing查詢的一種模糊匹配,就類似sql的模糊查詢方式 4. 消息產生者產生消息,把消息交給交換機 5. 交換機根據key的規則模糊匹配到對應的隊列,由隊列的監聽消費者接收消息消費

9、如何保證RabbitMQ消息的順序性?

  • 拆分多個 queue(消息隊列),每個 queue(消息隊列) 一個 consumer(消費者),就是多一些 queue(消息隊列)而已,確實是麻煩點;
  • 或者就一個 queue (消息隊列)但是對應一個 consumer(消費者),然後這個 consumer(消費者)內部用記憶體隊列做排隊,然後分發給底層不同的 worker 來處理。 

10、消息如何分發?

若該隊列至少有一個消費者訂閱,消息將以迴圈(round-robin)的方式發送給消費者。每條消息只會分發給一個訂閱的消費者(前提是消費者能夠正常處理消息併進行確認)。通過路由可實現多消費的功能。

11. 消息怎麼路由?

  • 消息提供方->路由->一至多個隊列消息發佈到交換器時,消息將擁有一個路由鍵(routing key),在消息創建時設定。通過隊列路由鍵,可以把隊列綁定到交換器上。消息到達交換器後,RabbitMQ 會將消息的路由鍵與隊列的路由鍵進行匹配(針對不同的交換器有不同的路由規則);
  • 常用的交換器主要分為一下三種:
    1. fanout:如果交換器收到消息,將會廣播到所有綁定的隊列上
    2. direct:如果路由鍵完全匹配,消息就被投遞到相應的隊列
    3. topic:可以使來自不同源頭的消息能夠到達同一個隊列。 使用 topic 交換器時,可以使用通配符

12、消息基於什麼傳輸?

由於 TCP 連接的創建和銷毀開銷較大,且併發數受系統資源限制,會造成性能瓶頸。RabbitMQ使用通道的方式來傳輸數據。通道是建立在真實的 TCP 連接內的虛擬連接,且每條 TCP 連接上的通道數量沒有限制。

12、消費時的冪等性?

  • 先說為什麼會重覆消費:正常情況下,消費者在消費消息的時候,消費完畢後,會發送一個確認消息給消息隊列,消息隊列就知道該消息被消費了,就會將該消息從消息隊列中刪除;
  • 但是因為網路傳輸等等故障,確認信息沒有傳送到消息隊列,導致消息隊列不知道自己已經消費過該消息了,再次將消息分發給其他的消費者。
  • 針對以上問題,一個解決思路是:保證消息的唯一性,就算是多次傳輸,不要讓消息的多次消費帶來影響;保證消息等冪性;
  1. 比如:在寫入消息隊列的數據做唯一標示,消費消息時,根據唯一標識判斷是否消費過;
  2. 假設你有個系統,消費一條消息就往資料庫里插入一條數據,要是你一個消息重覆兩次,你不就插入了兩條,這數據不就錯了?但是你要是消費到第二次的時候,自己判斷一下是否已經消費過了,若是就直接扔了,這樣不就保留了一條數據,從而保證了數據的正確性。

13、如何確保消息正確地發送至 RabbitMQ? 如何確保消息接收方消費了消息?

發送方確認模式

  • 將通道設置成 confirm 模式(發送方確認模式),則所有在通道上發佈的消息都會被指派一個唯一的 ID。
  • 一旦消息被投遞到目的隊列後,或者消息被寫入磁碟後(可持久化的消息),通道會發送一個確認給生產者(包含消息唯一 ID)。
  • 如果 RabbitMQ 發生內部錯誤從而導致消息丟失,會發送一條 nack(notacknowledged,未確認)消息。
  • 發送方確認模式是非同步的,生產者應用程式在等待確認的同時,可以繼續發送消息。當確認消息到達生產者應用程式,生產者應用程式的回調方法就會被觸發來處理確認消息。

接收方確認機制

  • 消費者接收每一條消息後都必須進行確認(消息接收和消息確認是兩個不同操作)。只有消費者確認了消息,RabbitMQ 才能安全地把消息從隊列中刪除。
  • 這裡並沒有用到超時機制,RabbitMQ 僅通過 Consumer 的連接中斷來確認是否需要重新發送消息。也就是說,只要連接不中斷,RabbitMQ 給了 Consumer 足夠長的時間來處理消息。保證數據的最終一致性;

下麵羅列幾種特殊情況

  1. 如果消費者接收到消息,在確認之前斷開了連接或取消訂閱,RabbitMQ 會認為消息沒有被分發,然後重新分發給下一個訂閱的消費者。(可能存在消息重覆消費的隱患,需要去重)
  2. 如果消費者接收到消息卻沒有確認消息,連接也未斷開,則 RabbitMQ 認為該消費者繁忙,將不會給該消費者分發更多的消息。

14、如何保證RabbitMQ消息的可靠傳輸?

  • 消息不可靠的情況可能是消息丟失,劫持等原因;
  • 丟失又分為:生產者丟失消息、消息列表丟失消息、消費者丟失消息;

1. 生產者丟失消息

  • 從生產者弄丟數據這個角度來看,RabbitMQ提供transaction和confirm模式來確保生產者不丟消息;
  • transaction機制就是說:發送消息前,開啟事務(channel.txSelect()),然後發送消息,如果發送過程中出現什麼異常,事務就會回滾(channel.txRollback()),如果發送成功則提交事務(channel.txCommit())。
  • 然而,這種方式有個缺點:吞吐量下降;
  • confirm模式用的居多:一旦channel進入confirm模式,所有在該通道上發佈的消息都將會被指派一個唯一的ID(從1開始),一旦消息被投遞到所有匹配的隊列之後;rabbitMQ就會發送一個ACK給生產者(包含消息的唯一ID),這就使得生產者知道消息已經正確到達目的隊列了;
  • 如果rabbitMQ沒能處理該消息,則會發送一個Nack消息給你,你可以進行重試操作。

2. 消息隊列丟數據:消息持久化。

  • 處理消息隊列丟數據的情況,一般是開啟持久化磁碟的配置。
  • 這個持久化配置可以和confirm機制配合使用,你可以在消息持久化磁碟後,再給生產者發送一個Ack信號。這樣,如果消息持久化磁碟之前,rabbitMQ陣亡了,那麼生產者收不到Ack信號,生產者會自動重發。
  • 那麼如何持久化呢?其實也很容易,就下麵兩步
    1. 將queue的持久化標識durable設置為true,則代表是一個持久的隊列。
    2. 發送消息的時候將deliveryMode=2這樣設置以後,即使rabbitMQ掛了,重啟後也能恢複數據。

3. 消費者丟失消息

  • 消費者丟數據一般是因為採用了自動確認消息模式,改為手動確認消息即可!
  • 消費者在收到消息之後,處理消息之前,會自動回覆RabbitMQ已收到消息;
  • 如果這時處理消息失敗,就會丟失該消息;解決方案:處理消息成功後,手動回覆確認消息。

15、為什麼不應該對所有的 message 都使用持久化機制?

  • 首先,必然導致性能的下降,因為寫磁碟比寫 RAM 慢的多,message 的吞吐量可能有 10 倍的差距。
  • 其次,message 的持久化機制用在 RabbitMQ 的內置 cluster 方案時會出現“坑爹”問題。矛盾點在於,若 message 設置了 persistent 屬性,但 queue 未設置 durable 屬性,那麼當該 queue 的owner node 出現異常後,在未重建該 queue 前,發往該 queue 的 message 將被 blackholed;若 message 設置了 persistent 屬性,同時 queue 也設置了 durable 屬性,那麼當 queue 的owner node 異常且無法重啟的情況下,則該 queue 無法在其他 node 上重建,只能等待其owner node 重啟後,才能恢復該 queue 的使用,而在這段時間內發送給該 queue 的 message將被 blackholed 。
  • 所以,是否要對 message 進行持久化,需要綜合考慮性能需要,以及可能遇到的問題。若想達到100,000 條/秒以上的消息吞吐量(單 RabbitMQ 伺服器),則要麼使用其他的方式來確保message 的可靠 delivery ,要麼使用非常快速的存儲系統以支持全持久化(例如使用 SSD)。另外一種處理原則是:僅對關鍵消息作持久化處理(根據業務重要程度),且應該保證關鍵消息的量不會導致性能瓶頸。

16、如何保證高可用的?RabbitMQ 的集群?

RabbitMQ 是比較有代表性的,因為是基於主從(非分散式)做高可用性的,我們就以 RabbitMQ為例子講解第一種 MQ 的高可用性怎麼實現。RabbitMQ 有三種模式:單機模式、普通集群模式、鏡像集群模式。

1. 單機模式

就是 Demo 級別的,一般就是你本地啟動了玩玩兒的,沒人生產用單機模式。

2. 普通集群模式

意思就是在多台機器上啟動多個 RabbitMQ 實例,每個機器啟動一個。 你創建的 queue,只會放在一個 RabbitMQ 實例上,但是每個實例都同步 queue 的元數據(元數據可以認為是 queue 的一些配置信息,通過元數據,可以找到 queue 所在實例)。你消費的時候,實際上如果連接到了另外一個實例,那麼那個實例會從 queue 所在實例上拉取數據過來。這方案主要是提高吞吐量的,就是說讓集群中多個節點來服務某個 queue 的讀寫操作。

3. 鏡像集群模式

  • 這種模式,才是所謂的 RabbitMQ 的高可用模式。跟普通集群模式不一樣的是,在鏡像集群模式下,你創建的 queue,無論元數據還是 queue 里的消息都會存在於多個實例上。就是說,每個 RabbitMQ 節點都有這個 queue 的一個完整鏡像,包含 queue 的全部數據的意思。然後每次你寫消息到 queue 的時候,都會自動把消息同步到多個實例的 queue 上。RabbitMQ 有很好的管理控制台,就是在後臺新增一個策略,這個策略是鏡像集群模式的策略,指定的時候是可以要求數據同步到所有節點的,也可以要求同步到指定數量的節點,再次創建 queue 的時候,應用這個策略,就會自動將數據同步到其他的節點上去了。
  • 這樣的好處在於,你任何一個機器宕機了,沒事兒,其它機器(節點)還包含了這個 queue的完整數據,別的 consumer 都可以到其它節點上去消費數據。壞處在於,第一,這個性能開銷也太大了吧,消息需要同步到所有機器上,導致網路帶寬壓力和消耗很重!RabbitMQ一個 queue 的數據都是放在一個節點里的,鏡像集群下,也是每個節點都放這個 queue 的完整數據。

17、如何解決消息隊列的延時以及過期失效問題?消息隊列滿了以後該怎麼處理?有幾百萬消息持續積壓幾小時,怎麼辦?

  • 消息積壓處理辦法:臨時緊急擴容。
  • 先修複 consumer 的問題,確保其恢復消費速度,然後將現有 cnosumer 都停掉。新建一個 topic,partition 是原來的 10 倍,臨時建立好原先 10 倍的 queue 數量。然後寫一個臨時的分發數據的 consumer 程式,這個程式部署上去消費積壓的數據,消費之後不做耗時的處理,直接均勻輪詢寫入臨時建立好的 10 倍數量的 queue。接著臨時徵用 10 倍的機器來部署 consumer,每一批 consumer 消費一個臨時 queue 的數據。這種做法相當於是臨時將 queue 資源和 consumer 資源擴大 10 倍,以正常的 10 倍速度來消費數據。等快速消費完積壓數據之後,得恢複原先部署的架構,重新用原先的 consumer 機器來消費消息。
  • MQ中消息失效:假設你用的是 RabbitMQ,RabbtiMQ 是可以設置過期時間的,也就是 TTL。如果消息在 queue 中積壓超過一定的時間就會被 RabbitMQ 給清理掉,這個數據就沒了。那這就是第二個坑了。這就不是說數據會大量積壓在 mq 里,而是大量的數據會直接搞丟。我們可以採取一個方案,就是批量重導,這個我們之前線上也有類似的場景乾過。就是大量積壓的時候,我們當時就直接丟棄數據了,然後等過了高峰期以後,比如大家一起喝咖啡熬夜到晚上12點以後,用戶都睡覺了。這個時候我們就開始寫程式,將丟失的那批數據,寫個臨時程式,一點一點的查出來,然後重新灌入 mq 裡面去,把白天丟的數據給他補回來。也只能是這樣了。假設 1 萬個訂單積壓在 mq 裡面,沒有處理,其中 1000 個訂單都丟了,你只能手動寫程式把那 1000 個訂單給查出來,手動發到 mq 里去再補一次。
  • mq消息隊列塊滿了:如果消息積壓在 mq 里,你很長時間都沒有處理掉,此時導致 mq 都快寫滿了,咋辦?這個還有別的辦法嗎?沒有,誰讓你第一個方案執行的太慢了,你臨時寫程式,接入數據來消費,消費一個丟棄一個,都不要了,快速消費掉所有的消息。然後走第二個方案,到了晚上再補數據吧。

18、設計MQ思路

  • 比如說這個消息隊列系統,我們從以下幾個角度來考慮一下;
  • 首先這個 mq 得支持可伸縮性,就是需要的時候快速擴容,就可以增加吞吐量和容量,那怎麼搞?設計個分散式的系統,參照一下 kafka 的設計理念,broker -> topic -> partition,每個partition 放一個機器,就存一部分數據。如果現在資源不夠了,給 topic 增加partition,然後做數據遷移,增加機器,不就可以存放更多數據,提供更高的吞吐量了?
  • 其次你得考慮一下這個 mq 的數據要不要落地磁碟吧?那肯定要了,落磁碟才能保證別進程掛了數據就丟了。那落磁碟的時候怎麼落啊?順序寫,這樣就沒有磁碟隨機讀寫的定址開銷,磁碟順序讀寫的性能是很高的,這就是 kafka 的思路。
  • 其次你考慮一下你的 mq 的可用性啊?這個事兒,具體參考之前可用性那個環節講解的 kafka 的高可用保障機制。多副本 -> leader & follower -> broker 掛了重新選舉 leader 即可對外服務。

19、RocketMq有什麼功能?

1、業務解耦:這也是發佈訂閱的消息模型。生產者發送指令到MQ中,然後下游訂閱這類指令的消費者會收到這個指令執行相應的邏輯,整個過程與具體業務無關,抽象成了一個發送指令,存儲指令,消費指令的過程。 2、前端削峰:前端發起的請求在短時間內太多後端無法處理,可以堆積在MQ中,後端按照一定的順序處理,秒殺系統就是這麼實現的。 3、億級消息的堆積能力,單個隊列中的百萬級消息的累積容量。 4、高可用性:Broker伺服器支持多Master多Slave的同步雙寫以及Master多Slave的非同步複製模式,其中同步雙寫可保證消息不丟失。 5、高可靠性:生產者將消息發送到Broker端有三種方式,同步、非同步和單向,其中同步和非同步都可以保證消息成功的成功發送。Broker在對於消息刷盤有兩種策略:同步刷盤和非同步刷盤,其中同步刷盤可以保證消息成功的存儲到磁碟中。消費者的消費模式也有集群消費和廣播消費兩種,預設集群消費,如果集群模式中消費者掛了,一個組裡的其他消費者會接替其消費。綜上所述,是高可靠的。 6、支持分散式事務消息:這裡是採用半消息確認和消息回查機制來保證分散式事務消息的。 7、支持消息過濾:建議採用消費者業務端的tag過濾。 8、支持順序消息:消息在Broker中是採用隊列的FIFO模式存儲的,也就是發送是順序的,只要保證消費的順序性即可。 9、支持定時消息和延遲消息:Broker中由定時消息的機制,消息發送到Broker中,不會立即被Consumer消費,會等到一定的時間才被消費。延遲消息也是一樣,延遲一定時間之後才會被Consumer消費。

20、RoctetMq的架構

RocketMq一共有四個部分組成:NameServer,Broker,Producer生產者,Consumer消費者,每一部分都是集群部署的。

NameServer

NameServer是一個無狀態的伺服器,角色類似於Dubbo的Zookeeper,但比Zookeeper更輕量。 特點:
  • 每個NameServer結點之間是相互獨立,彼此沒有任何信息交互。
  • Nameserver被設計成幾乎是無狀態的,通過部署多個結點來標識自己是一個偽集群,Producer在發送消息前從NameServer中獲取Topic的路由信息也就是發往哪個Broker,Consumer也會定時從NameServer獲取topic的路由信息,Broker在啟動時會向NameServer註冊,並定時進行心跳連接,且定時同步維護的Topic到NameServer。
功能主要有兩個:
  • 1、跟Broker結點保持長連接。
  • 2、維護Topic的路由信息。

Broker

消息存儲和中轉角色,負責存儲和轉發消息。
  • Broker內部維護著一個個Message Queue,用來存儲消息的索引,真正存儲消息的地方是CommitLog(日誌文件)。
  • 單個Broker與所有的Nameserver保持著長連接和心跳,並會定時將Topic信息同步到NameServer,和NameServer的通信底層是通過Netty實現的。

Producer

消息生產者,業務端負責發送消息,由用戶自行實現和分散式部署。 Producer的負載均衡 Producer的負載均衡是由MQFaultStratege.selectOneMessageQueue()來實現的。這個方法就是隨機選擇一個要發送消息的broker來達到負載均衡的效果,選擇的標準:儘量不選剛剛選過的broker,儘量不選發送上條消息延遲過高或沒有響應的broker,也就是找到一個可用的broker。 Producer發送的三種策略 Producer發送消息有三種方式:同步、非同步和單向
  1. 同步:同步發送是指發送方發出數據後等待接收方發迴響應後在發送下一個數據包。一般用於重要的消息通知,如重要的通知郵件或者營銷簡訊等。
  2. 非同步:非同步發送是指發送方發出數據後不等接收方發迴響應就發出下一個數據包。一般用於可能鏈路耗時較長而對響應時間比較敏感的場景。如視頻上傳後通知啟動轉碼服務。
  3. 單向:單向發送是指只負責發送消息而不等待接收方發送響應且沒有回調函數,適合那些耗時比較短且對可靠性要求不高的場景,例如日誌收集。 

Consumer

消息消費者,負責消費消息,由用戶自行實現併進行集群部署。 推拉消費模式
  • PULL:拉取型消費者主動從broker中拉取消息消費,只要拉取到消息,就會啟動消費過程,稱為主動型消費。
  • PUSH:推送型消費者就是要註冊消息的監聽器,監聽器是要用戶自行實現的。當消息達到broker伺服器後,會觸發監聽器拉取消息,然後啟動消費過程。但是從實際上看還是從broker中拉取消息,稱為被動消費型。
集群 & 廣播 看業務需求,預設是集群消費。
  • 集群消費:broker中的一條消息會發送給訂閱這個topic的一個消費組裡的唯一一個消費者進行消費。如果這個消費者掛掉了,組裡的其他消費者會接替它進行消費。
  • 廣播消費:broker中的一條消息會發送給訂閱這個topic的一個消費組裡的每一個消費者進行消費。
Consumer的負載均衡
  • Consumer的負載均衡是指將MessageQueue中的消息隊列分配到消費者組裡的具體消費者。
  • Consumer在啟動的時候會實例化rebalanceImpl,這個類負責消費端的負載均衡。通過rebalanceImpl調用allocateMesasgeQueueStratage.allocate()完成負載均衡。
  • 每次有新的消費者加入到組中就會重新做一下分配。每10秒自動做一次負載均衡。 

21、RocketMq消息模型

Message

就是要傳輸的消息,一個消息必須有一個主題,一條消息也可以有一個可選的Tag(標簽)和額外的鍵值對,可以用來設置一個業務的key,便於開發中在broker服務端查找消息。

Topic

主題,是消息的第一級類型,每條消息都有一個主題,就像信件郵寄的地址一樣。主題就是我們具體的業務,比如一個電商系統可以有訂單消息,商品消息,採購消息,交易消息等。Topic和生產者和消費者的關係非常鬆散,生產者和Topic可以是1對多,多對1或者多對多,消費者也是這樣。

Tag

標簽,是消息的第二級類型,可以作為某一類業務下麵的二級業務區分,它的主要用途是在消費端的消息過濾。比如採購消息分為採購創建消息,採購審核消息,採購推送消息,採購入庫消息,採購作廢消息等,這些消息是同一Topic和不同的Tag,當消費端只需要採購入庫消息時就可以用Tag來實現過濾,不是採購入庫消息的tag就不處理。

Group

組,可分為ProducerGroup生產者組合ConsumerGroup消費者組,一個組可以訂閱多個Topic。一般來說,某一類相同業務的生產者和消費者放在一個組裡。

Message Queue

消息隊列,一個Topic可以劃分成多個消息隊列。Topic只是個邏輯上的概念,消息隊列是消息的物理管理單位,當發送消息的時候,Broker會輪詢包含該Topic的所有消息隊列,然後將消息發出去。有了消息隊列,可以使得消息的存儲可以分散式集群化,具有了水平的擴展能力。 

offset

是指消息隊列中的offffset,可以認為就是下標,消息隊列可看做數組。offffset是java long型,64位,理論上100年不會溢出,所以可以認為消息隊列是一個長度無限的數據結構。 

22、如何保證順序消息?

  • 順序由producer發送到broker的消息隊列是滿足FIFO的,所以發送是順序的,單個queue里的消息是順序的。多個Queue同時消費是無法絕對保證消息的有序性的。所以,同一個topic,同一個queue,發消息的時候一個線程發送消息,消費的時候一個線程去消費一個queue里的消息。
  • 追問:怎麼保證消息發到同一個queue里?RocketMQ給我們提供了MessageQueueSelector介面,可以重寫裡面的介面,實現自己的演算法,比如判斷i%2==0,那就發送消息到queue1否則發送到queue2。

23、如何實現消息過濾?

  1. 有兩種方案,一種是在broker端按照Consumer的去重邏輯進行過濾,這樣做的好處是避免了無用的消息傳輸到Consumer端,缺點是加重了Broker的負擔,實現起來相對複雜。
  2. 另一種是在Consumer端過濾,比如按照消息設置的tag去重,這樣的好處是實現起來簡單,缺點是有大量無用的消息到達了Consumer端只能丟棄不處理。

24、如何實現消息去重?

  • 如果由於網路等原因,多條重覆消息投遞到了Consumer端,你怎麼進行消息去重?
  • 這個得先說下消息的冪等性原則:就是用戶對於同一種操作發起的多次請求的結果是一樣的,不會因為操作了多次就產生不一樣的結果。只要保持冪等性,不管來多少條消息,最後處理結果都一樣,需要Consumer端自行實現。
  • 去重的方案:因為每個消息都有一個MessageId, 保證每個消息都有一個唯一鍵,可以是資料庫的主鍵或者唯一約束,也可以是Redis緩存中的鍵,當消費一條消息前,先檢查資料庫或緩存中是否存在這個唯一鍵,如果存在就不再處理這條消息,如果消費成功,要保證這個唯一鍵插入到去重表中。

25、分散式事務消息?

  • 你知道半消息嗎?RocketMQ是怎麼實現分散式事務消息的?
  • 半消息:是指暫時還不能被Consumer消費的消息,Producer成功發送到broker端的消息,但是此消息被標記為“暫不可投遞”狀態,只有等Producer端執行完本地事務後經過二次確認了之後,Consumer才能消費此條消息。 

上圖就是分散式事務消息的實現過程,依賴半消息,二次確認以及消息回查機制。 1、Producer向broker發送半消息。 2、Producer端收到響應,消息發送成功,此時消息是半消息,標記為“不可投遞”狀態,Consumer消費不了。 3、Producer端執行本地事務。 4、正常情況本地事務執行完成,Producer向Broker發送Commit/Rollback,如果是Commit,Broker端將半消息標記為正常消息,Consumer可以消費,如果是Rollback,Broker丟棄此消息。 5、異常情況,Broker端遲遲等不到二次確認。在一定時間後,會查詢所有的半消息,然後到Producer端查詢半消息的執行情況。 6、Producer端查詢本地事務的狀態。 7、根據事務的狀態提交commit/rollback到broker端。(5,6,7是消息回查) 

26、消息的可用性?

RocketMQ如何能保證消息的可用性/可靠性?(這個問題的另一種問法:如何保證消息不丟失)要從Producer,Consumer和Broker三個方面來回答。 從Producer角度分析,如何確保消息成功發送到了Broker?
  • 1、可以採用同步發送,即發送一條數據等到接受者返迴響應之後再發送下一個數據包。如果返迴響應OK,表示消息成功發送到了broker,狀態超時或者失敗都會觸發二次重試。
  • 2、可以採用分散式事務消息的投遞方式。
  • 3、如果一條消息發送之後超時,也可以通過查詢日誌的API,來檢查是否在Broker存儲成功。總的來說,Producer還是採用同步發送來保證的。
從Broker角度分析,如何確保消息持久化?
  • 1、消息只要持久化到CommitLog(日誌文件)中,即使Broker宕機,未消費的消息也能重新恢復再消費。
  • 2、Broker的刷盤機制:同步刷盤和非同步刷盤,不管哪種刷盤都可以保證消息一定存儲在pagecache中(記憶體中),但是同步刷盤更可靠,它是Producer發送消息後等數據持久化到磁碟之後再返迴響應給Producer。
  • 3、Broker支持多Master多Slave同步雙寫和多Master多Slave非同步複製模式,消息都是發送給Master主機,但是消費既可以從Master消費,也可以從Slave消費。同步雙寫模式可以保證即使Master宕機,消息肯定在Slave中有備份,保證了消息不會丟失。
從Consumer角度分析,如何保證消息被成功消費? Consumer自身維護了個持久化的offset(對應Message Queue里的min offffset),用來標記已經成功消費且已經成功發回Broker的消息下標。如果Consumer消費失敗,它會向Broker發回消費失敗的狀態,發回成功才會更新自己的offffset。如果發回給broker時broker掛掉了,Consumer會定時重試,如果Consumer和Broker一起掛掉了,消息還在Broker端存儲著,Consumer端的offset也是持久化的,重啟之後繼續拉取fffset之前的消息進行消費。 

25、刷盤實現

RocketMQ提供了兩種刷盤策略:同步刷盤非同步刷盤
  • 同步刷盤:在消息達到Broker的記憶體之後,必須刷到commitLog日誌文件中才算成功,然後返回Producer數據已經發送成功。
  • 非同步刷盤:非同步刷盤是指消息達到Broker記憶體後就返回Producer數據已經發送成功,會喚醒一個線程去將數據持久化到CommitLog日誌文件中。
  • 優缺點分析:同步刷盤保證了消息不丟失,但是響應時間相對非同步刷盤要多出10%左右,適用於對消息可靠性要求比較高的場景。非同步刷盤的吞吐量比較高,RT小,但是如果broker斷電了記憶體中的部分數據會丟失,適用於對吞吐量要求比較高的場景。 

 

 

昔我往矣

楊柳依依

今我來思

 雨雪霏霏 

 

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 項目同步git:https://gitee.com/lixin_ajax/vue3-vite-ts-pinia-vant-less.git 覺得有幫助的小伙伴請點下小心心哦 為避免贅述,過於基礎的點會直接省略或貼圖,比如創建文件夾/文件的路徑/路由一類 配置相應功能,也儘量只貼相關代碼,並不代表整個 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 視頻通話SDK用的即構的,uniapp插件市場地址 推送用的極光的,uniapp插件市場地址 即構音視頻SDK uniapp插件市場的貌似是有些問題,導入不進項目,直接去官網下載,然後放到項目下的 nativeplugins 目錄下,在配 ...
  • 在工作流頁面中,除了特定的業務表單信息外,往往也需要同時展示通用申請單的相關信息,因此在頁面設計的時候需要使用一些組件化的概念來實現動態的內容展示處理,本篇隨筆介紹Vue3+TypeScript+ElementPus的前端工作流模塊中實現統一的表單編輯和表單詳情查看處理。 ...
  • 前言: 昨天我們學習了 TS 的數據類型,不知道大家回去以後練習沒練習,如果你練習了一定會發現一個問題,我們的 TS 好像和 JS 不太一樣 JS 寫完之後直接就可以放到頁面上,就可以用了,而我們的 TS 需要用 tsc 編譯一下,編譯為 JS 才能在頁面中使用 這時就會有同學說了,誒呀,六扇老師, ...
  • 限流,通常講就是限制流量,也有很多其他的說法,比如:限頻、疲勞度控制等。 原文鏈接:自定義開發限流組件 之 場景需求分析-一隻小Coder 最近遇到一個需求,系統A作為一個專門推送消息給客戶的消息中心系統,對於每個客戶是否能接受消息,能接受多少消息,接收消息的速度,能接受哪些消息等都要進行控制,這也 ...
  • 5.4 介面開發-根據id刪除附件 第2-1-2章 傳統方式安裝FastDFS-附FastDFS常用命令 第2-1-3章 docker-compose安裝FastDFS,實現文件存儲服務 第2-1-5章 docker安裝MinIO實現文件存儲服務-springboot整合minio-minio全網最 ...
  • 自己的客服系統做好了,官網頁面也有了,但是沒有介紹性的內容文章。網站被收錄的太少,這樣會導致網站的權重不高,搜索排名比較低。 因此要簡單的加上一個小型的內容管理功能。 設計資料庫 很簡單的兩張表,分類表和內容表 DROP TABLE IF EXISTS `cms_cate`; CREATE TABL ...
  • jdk線程池工作原理解析(二) 本篇博客是jdk線程池ThreadPoolExecutor工作原理解析系列博客的第二篇,在第一篇博客中從源碼層面分析了ThreadPoolExecutor在RUNNING狀態下處理任務的核心邏輯,而在這篇博客中將會詳細講解jdk線程池ThreadPoolExecuto ...
一周排行
    -Advertisement-
    Play Games
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...