高級程序員知識學習(RabbitMQ相關知識)

RabbitMQ高級級消息隊列協議。是一種的跨進程的通信的機制,用於上下游之間的管道方式的傳遞的消息。RabbitMQ的作用是解耦、異步、削峯。

爲什麼使用MQ?MQ的優點:

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

MQ的缺點:

系統可用性降低:本來系統運行好好的,現在你非要加入個消息隊列進去,那消息隊列掛了,你的系統不是呵呵了。因此,系統可用性會降低;

系統複雜度提高:加入了消息隊列,要多考慮很多方面的問題,比如:一致性問題、如何保證消息不被重複消費、如何保證消息可靠性傳輸等。因此,需要考慮的東西更多,複雜性增大。

一致性問題:A 系統處理完了直接返回成功了,人都以爲你這個請求就成功了;但是問題是,要是 BCD 三個系統那裏,BD 兩個系統寫庫成功了,結果 C系統寫庫失敗了,咋整?你這數據就不一致了。

RabbitMQ的原理

AMQP協議: 高級消息隊列協議

應用程序和Rabbit Server之間會創建一個TCP連接,一旦TCP打開,並通過了認證,認證就是你試圖連接Rabbit之前發送的Rabbit服務器連接信息和用戶名和密碼,有點像程序連接數據庫,認證通過你的應用程序和Rabbit就創建了一條AMQP信道(Channel)。信道是創建在“真實”TCP上的虛擬連接,AMQP命令都是通過信道發送出去的,每個信道都會有一個唯一的ID,不論是發佈消息,訂閱隊列或者介紹消息都是通過信道完成的。

包括:ConnectionFactory(連接管理器)、Channel(信道)、Exchange(交換器)、Queue(隊列)、RoutingKey(路由鍵)、BindingKey(綁定鍵)。

ConnectionFactory(連接管理器):應用程序與Rabbit之間建立連接的管理器,程序代碼中使用;

Channel(信道):消息推送使用的通道;

Exchange(交換器):用於接受、分配消息;

Queue(隊列):用於存儲生產者的消息;

RoutingKey(路由鍵):用於把生成者的數據分配到交換器上;

BindingKey(綁定鍵):用於把交換器的消息綁定到隊列上;

RabbitMQ-理解消息通信-虛擬主機和隔離

Virtual Host的資源的隔離:

每個RabbitMQ服務器都能創建虛擬的消息服務器,我們稱之爲虛擬主機(vhost)每一個vhost本質上是一個mini版的RabbitMQ服務器,擁有自己的隊列、交換器和綁定等等。更重要的是,他擁有自己的權限機制,這使得你能夠安全地使用一個RabbitMQ服務器來服務衆多的應用程序。vhost就像是虛擬機之與物理服務器一樣:他們在各個實例間提供邏輯上的分離,允許你爲不同程序安全保密地運行數據,它既能將同一個Rabbit的衆多客戶區分開來,又可以避免隊列和交換器命名衝突。

vhost是AMQP概念的基礎,你必須在連接時進行指定。RabbitMQ包含了一個開箱即用的默認vhost“/”,如果你不需要多個vhost,那麼就使用默認的吧,使用缺省的guest用戶名和密碼guest就可以訪問默認的vhost。

當你在RabbitMQ集羣上創建vhost,整個集羣上都會創建該vhost,vhost不僅消除了爲基礎架構中的每一層運行一個RabbitMQ服務器的需要,同樣也避免了爲每一層創建不同集羣VHost:vhost去做第一層的區分,虛擬主機,工作組等,它默認是/

Channel:創建了客戶端到Broker之間的連接後,客戶端還是不能發送消息的。需要爲每一個Connection創建Channel,AMQP協議規定只有通過Channel才能執行AMQP的命令。一個Connection可以包含多個Channel。之所以需要Channel,是因爲TCP連接的建立和釋放都是十分昂貴的,如果一個客戶端每一個線程都需要與Broker交互,如果每一個線程都建立一個TCP連接,暫且不考慮TCP連接是否浪費,操作系統也無法承受每秒建立如此多的TCP連接。

RabbitMQ的消息路由機制

Exchange的路由協議:

1 Direct exchange

2 Topic exchange(可以使用通配符)

3 Fanout exchange:

RabbitMQ中的消息可靠性機制

MQ中可能存在消息消息的丟失。其中消息的丟失又分爲:生產者丟失消息、消息列表丟失消息、消費者丟失消息。消息不可靠的情況可能是消息丟失,劫持等原因;

生產者丟失消息:(解決方案:RabbitMQ提供transaction和confirm模式

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

消息隊列丟數據:(解決方案:消息持久化。處理消息隊列丟數據的情況,一般是開啓持久化磁盤的配置。

這個持久化配置可以和confirm機制配合使用,你可以在消息持久化磁盤後,再給生產者發送一個Ack信號。這樣,如果消息持久化磁盤之前,rabbitMQ陣亡了,那麼生產者收不到Ack信號,生產者會自動重發。

那麼如何持久化呢?1將queue的持久化標識durable設置爲true,則代表是一個持久的隊列2發送消息的時候將deliveryMode=2。

這樣設置以後,即使rabbitMQ掛了,重啓後也能恢復數據

消費者丟失消息:(解決方案:消費者丟數據一般是因爲採用了自動確認消息模式,改爲手動確認消息即可!

消費者在收到消息之後,處理消息之前,會自動回覆RabbitMQ已收到消息;如果這時處理消息失敗,就會丟失該消息;解決方案:處理消息成功後,手動回覆確認消息。

RabbitMQ中的如何保證消息的可靠性的解決方案:

1保證消息不丟失(三步)

1開啓事務(不推薦)
2開啓confirm(推薦)
3開啓RabbitMQ持久化(交換機、隊列、消息)
4關閉RabbitMQ自動ack(改成手動)

2、保證消息不重複消費
1冪等性(每個消息用一個唯一標識來區分,消費前先判斷標識有沒有被消費過,若已消費過,則直接ACK)

3、RabbitMQ如何保證消息的順序性
將消息放入同一個交換機,交給同一個隊列,這個隊列只有一個消費者,消費者只允許同時開啓一個線程

4RabbitMQ消息重試機制
消費者在消費消息的時候,如果消費者業務邏輯出現程序異常,這時候應該如何處理?
答案:使用消息重試機制(SpringBoot默認3次消息重試機制)

如何合適選擇重試機制:1消費者取到消息後,調用第三方接口,接口無法訪問,需要使用重試機制2消費者取到消息後,拋出數據轉換異常,不需要重試機制,需要發佈者進行解決。

5、SpringBoot消息重試機制
@EnableRetry註解:表示啓用重試機制(value表示哪些異常需要觸發重試,maxAttempts設置最大重試次數,delay表示重試的延遲時間,multiplier表示上一次延時時間是這一次的倍數)@Retryable(value = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 2000, multiplier = 1.5))

@Recover註解:當重試次數達到設置的最大次數的時候,程序還是執行異常,調用的回調函數。

6、RabbitMQ死信隊列:死信隊列是當消息在一個隊列因爲下列原因:

a、消息被拒絕(basic.reject或basic.nack)並且requeue=false.

b、消息TTL過期

c、隊列達到最大長度(隊列滿了,數據無法添加到mq中)

變成了“死信隊列”後被重新投遞(publish)到另一個Exchange,然後重新消費。說白了就是沒有被消費的消息換個地方重新被消費

7、RabbitMQ解決分佈式事務

經典案例,以目前流行的外賣爲例,用戶下單後,調用訂單服務,訂單服務調用派單系統通知送外賣人員送單,這時候訂單系統與派單系統採用MQ異步通訊。

RabbitMQ解決分佈式事務原理。答案:採用最終一致性原理,就是採用弱一致性原理。

需要保證以下三要素:

a、確保生產者一定要將數據投遞到MQ服務器中(採用MQ消息確認機制)

b、確保消費者能夠正確消費消息,採用手動ACK模式(注意重試、冪等性問題)

c、如何保證第一個事務先執行,採用補償機制,在創建一個補單消費者進行監聽,如果訂單沒有創建成功,進行補單。(如果第一個事務中出錯,補單消費者會在重新執行一次第一個事務,例如第一個事務是添加訂單表,如果失敗在補單的時候重新生成訂單記錄,由於訂單號唯一,所以不會重複)

RabbitMQ的消息補償機制

除了以上的保障措施之外,爲了防止生產者發送消息失敗或者接收 RabbitMQ confirm 的時候網絡斷掉等,我們還需要一套完善的消息補償機制,接下來我們會介紹目前業界主流的兩種方案。

消息落庫,對消息進行狀態標記

https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9pYm0yc2I1M2xSaHo0NTV0eTJtWmdYOWFjQXNYeEd1NkYycm53VEFoUUk3ZzNrRkpib291MXd4TUJvaWJBZGhJd0lBTllCY3BPNGVRRDNXalNMVWJZenpnLzY0MA?x-oss-process=image/format,png

Step 1:首先生產端處理完業務數據,然後在發送消息前先持久化到消息記錄表

Step 2:發送消息給 RabbitMQ,採用 confirm 機制

Step 3:監聽 RabbitMQ 的 confirm 回調

Step 4:根據 message id 以及回調的信息更新消息狀態

Step 5:分佈式定時任務獲取未發送成功的消息,然後判斷重試次數是否大於 N 次,N 爲業務系統定義的最大重試次數

Step 6:將重試次數 < N 次的消息重新發送給 RabbitMQ

Step 7:將重試次數 > N 次的消息降級爲人工處理

這種方案能保證即使消息發送失敗了,或者其中某一環突然掉鏈子了,但只要消息成功入庫了,就能通過定時任務重試機制發送給 RabbitMQ,然後消費端再通過 ack 機制去消費消息,需要注意的是,消費端的消費邏輯必須冪等。

延遲投遞,做二次確認,回調檢查

https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9pYm0yc2I1M2xSaHo0NTV0eTJtWmdYOWFjQXNYeEd1NkZHd1lFY1VrUGFJNGpuaWEzYmpOcHdPbTNvdWljVEt1TU9SNVlCYmRtN2FtenMzNmliUjNsd3FxdmcvNjQw?x-oss-process=image/format,png

Step 1:發送消息給 RabbitMQ

Step 2:發送一條一樣的延遲消息給 Callback Server

Step 3:消費者消費消息

Step 4:如果消息消費成功了,則將消息發送給 Callback Server,注意這裏不是採用 ack 機制

Step 5:Callback Server 監聽到消費者發來的消息,知道有條消息消費成功了,然後將消息記錄到 DB

Step 6:Callback Server 監聽生產端發來的延遲消息

Step 7:Callback Server 到數據庫查詢消息是否存在

Step 8:如果消息存在,則不做處理,如果消息不存在,則發起一個 RPC 請求到生產端,告訴它消息發送失敗了,需要重新發送一遍,然後生產端重新發送即時和延遲兩條消息出去,按照流程再走一遍。

上面那種方案的弊端是顯而易見的,在整個消息發送以及確認流程中,每條消息都需要入庫再發送,然後收到 confirm 去更新消息狀態,如果在高併發場景下,數據庫毫無疑問會成爲一個瓶頸。而相較於上面的方案,延遲投遞二次確認的方案對數據庫的操作更少,並且對正常消息的發送和消費過程不會有太多幹預,但是如果在沒有消費者或者消費者全部斷開連接的情況下會造成大量重複消息積壓,所以一般在高併發場景下配合一些的策略使用。

RabbitMQ的消息持久化原理

1.交換器的持久化

交換器的持久化是在聲明交換器的時候,將durable設置爲true。如果交換器不設置持久化,那麼在RabbitMQ交換器服務重啓之後,相關的交換器信息會丟失,不過消息不會丟失,但是不能將消息發送到這個交換器。

2.隊列對持久化

隊列的持久化在聲明隊列的時候,將durable設置爲true。如果隊列不設置持久化,那麼RabbitMQ交換器服務重啓之後,相關的隊列信息會丟失,同時隊列中的消息也會丟失。

3.消息的持久化

消息的持久化是在BasicProperties中設置deliveryMode設置爲2。隊列的持久化能保證本身的元數據不會因爲異常而丟失,但是不能保證內部所存在的消息不會丟失。要確保消息不丟失,需要將消息持久化。

如果將所有的消息都進行持久化操作,這樣會嚴重影響RabbitMQ的性能。寫入磁盤的速度比寫入內存的速度慢很多。所以要在可靠性和吞吐量之間做權衡。

將交換器、隊列和消息都進行持久化操作後,也並不能保證消息一定不會丟失。

1.對於消費者來說,如果在訂閱消息的時候,將autoAck設置爲true,那麼消費者接收到消息後,還沒有處理,就出現了異常掛掉了,此時,隊列中已經將消息刪除,消費者不能夠在收到消息。這種情況可以將autoAck設置爲false,進行手動確認。

2.在持久化後的消息存入Rabbit'MQ之後,還需要一段時間才能存入磁盤。RabbitMQ並不會爲每條消息都進行同步存盤,可能僅僅是保存到操作系統緩存之中而不是物理磁盤。如果在這段時間,服務器宕機或者重啓,消息還沒來得及保存到磁盤當中,從而丟失。對於這種情況,可以引入RabiitMQ鏡像隊列機制。

RabbitMQ的高可用集羣原理

採用三個節點組成了一個RabbitMQ的集羣,Exchange A的元數據信息在所有節點上是一致的,而Queue(存放消息的隊列)的完整數據則只會存在於它所創建的那個節點上。,其他節點只知道這個queue的metadata信息和一個指向queue的owner node的指針。

RabbitMQ集羣元數據的同步:

RabbitMQ集羣會始終同步四種類型的內部元數據(類似索引

隊列元數據:隊列名稱和它的屬性,

交換器元數據:交換器名稱、類型和屬,

綁定元數據:一張簡單的表格展示瞭如何將消息路由到隊列,

vhost元數據:爲vhost內的隊列、交換器和綁定提供命名空間和安全屬性;

因此,當用戶訪問其中任何一個RabbitMQ節點時,通過rabbitmqctl查詢到的queue/user/exchange/vhost等信息都是相同的。

爲何RabbitMQ集羣僅採用元數據同步的方式

1存儲空間,如果每個集羣節點都擁有所有Queue的完全數據拷貝,那麼每個節點的存儲空間會非常大,集羣的消息積壓能力會非常弱(無法通過集羣節點的擴容提高消息積壓能力);2性能,消息的發佈者需要將消息複製到每一個集羣節點,對於持久化消息,網絡和磁盤同步複製的開銷都會明顯增加。

RabbitMQ的集羣的工作原理圖

場景1:客戶端直接連接隊列所在節點

如果有一個消息生產者或者消息消費者通過amqp-client的客戶端連接至節點1進行消息的發佈或者訂閱,那麼此時的集羣中的消息收發只與節點1相關,這個沒有任何問題;如果客戶端相連的是節點2或者節點3(隊列1數據不在該節點上),那麼情況又會是怎麼樣呢?

場景2:客戶端連接的是非隊列數據所在節點

如果消息生產者所連接的是節點2或者節點3,此時隊列1的完整數據不在該兩個節點上,那麼在發送消息過程中這兩個節點主要起了一個路由轉發作用,根據這兩個節點上的元數據(也就是上文提到的:指向queue的owner node的指針)轉發至節點1上,最終發送的消息還是會存儲至節點1的隊列1上。

鏡像隊列?

RabbitMQ的事務管理

正常情況下,如果消息經過交換器進入隊列就可以完成消息的持久化,但如果消息在沒有到達broker之前出現意外,那就造成消息丟失,有沒有辦法可以解決這個問題?

RabbitMQ有兩種方式來解決這個問題:1通過AMQP提供的事務機制實現;2使用發送者確認模式實現。

事務使用:事務的實現主要是對信道(Channel)的設置,主要的方法有三個:

    channel.txSelect()聲明啓動事務模式;

    channel.txComment()提交事務;

    channel.txRollback()回滾事務;

假設消費者模式中使用了事務,並且在消息確認之後進行了事務回滾,那麼RabbitMQ會產生什麼樣的變化?結果分爲兩種情況:

1、autoAck=false手動應對的時候是支持事務的,也就是說即使你已經手動確認了消息已經收到了,但在確認消息會等事務的返回解決之後,在做決定是確認消息還是重新放回隊列,如果你手動確認現在之後,又回滾了事務,那麼已事務回滾爲主,此條消息會重新放回隊列;

2、autoAck=true如果自定確認爲true的情況是不支持事務的,也就是說你即使在收到消息之後在回滾事務也是於事無補的,隊列已經把消息移除了;

二、Confirm發送方確認模式

方式一:channel.waitForConfirms()普通發送方確認模式;

方式二:channel.waitForConfirmsOrDie()批量確認模式;

方式三:channel.addConfirmListener()異步監聽發送方確認模式;

RabbitMQ的常見問題和解決方案

MQ 的常見問題有:消息的順序問題、消息的重複問題

消息的順序問題:消息有序指的是可以按照消息的發送順序來消費。

假如生產者產生了 2 條消息:M1、M2,假定 M1 發送到 S1,M2 發送到 S2,如果要保證 M1 先於 M2 被消費,怎麼做?

https://imgconvert.csdnimg.cn/aHR0cHM6Ly91c2VyLWdvbGQtY2RuLnhpdHUuaW8vMjAxOS81LzQvMTZhODMwNzVlMjc1NTFiMA?x-oss-process=image/format,png

解決方案:保證生產者 - MQServer - 消費者是一對一對一的關係

缺陷:

    並行度就會成爲消息系統的瓶頸(吞吐量不夠)

    更多的異常處理,比如:只要消費端出現問題,就會導致整個處理流程阻塞,我們不得不花費更多的精力來解決阻塞的問題。 (2)通過合理的設計或者將問題分解來規避。

    不關注亂序的應用實際大量存在

    隊列無序並不意味着消息無序 所以從業務層面來保證消息的順序而不僅僅是依賴於消息系統,是一種更合理的方式

消息的重複問題 造成消息重複的根本原因是:網絡不可達

所以解決這個問題的辦法就是繞過這個問題。那麼問題就變成了:如果消費端收到兩條一樣的消息,應該怎樣處理?

消費端處理消息的業務邏輯保持冪等性。只要保持冪等性,不管來多少條重複消息,最後處理的結果都一樣。保證每條消息都有唯一編號且保證消息處理成功與去重表的日誌同時出現。利用一張日誌表來記錄已經處理成功的消息的 ID,如果新到的消息 ID 已經在日誌表中,那麼就不再處理這條消息。

MQ常問道的面試問題

爲什麼不通過TCP直接發送命令?

對於操作系統來說創建和銷燬TCP會話是非常昂貴的開銷,假設高峯期每秒有成千上萬條連接,每個連接都要創建一條TCP會話,這就造成了TCP連接的巨大浪費,而且操作系統每秒能創建的TCP也是有限的,因此很快就會遇到系統瓶頸。如果我們每個請求都使用一條TCP連接,既滿足了性能的需要,又能確保每個連接的私密性,這就是引入信道概念的原因

RabbitMQ的消息確認機制:也是RabbitMQ中的事務機制。

在RocketMQ裏事務消息處理分爲三個個階段

第一階段是把消息傳遞給MQ,但是消息對消費端不可見,實際上數據已經發送到了broker上,會拿到消息的地址。(發送prepared消息)

第二階段是執行本地事務。

第三個階段是通過第一階段拿到的地址去訪問消息,並修改消息的狀態。如果本地消息處理成功返回commit,則修改在broker上的消息的狀態爲對消費端可見,如果失敗返回rollback,則對消費端不可見。(發送確認消息給broker)

爲了防止確認消息發送失敗,RocketMQ會定期掃描集羣中的事務消息,如果發現有prepared消息(狀態沒有改變),它會向生產者確認,該消息到底是否繼續發送給消費端,這樣就保證了本地事務和消息發送的同時成功或者同時失敗。

https://img-blog.csdn.net/20180730153556356?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NtdWdhb3lp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70

如果你要確保說寫RabbitMQ的消息別丟,可以開啓confirm模式,在生產者那裏設置開啓confirm模式之後,你每次寫的消息都會分配一個唯一的id,然後如果寫入了RabbitMQ中,RabbitMQ會給你回傳一個ack消息,告訴你說這個消息ok了。如果RabbitMQ沒能處理這個消息,會回調你一個nack接口,告訴你這個消息接收失敗,你可以重試。而且你可以結合這個機制自己在內存裏維護每個消息id的狀態,如果超過一定時間還沒接收到這個消息的回調,那麼你可以重發。

怎麼保證消息不重複消費?

給每一個消息添加一個全局唯一的id。消費者接收到消息時,將消息對象進行MD5加密,作爲消息唯一性。如果發現messageObj(發送到mq的數據)已經存在,則忽略,進而保證消息不被重複消費。

怎解決消息的擠壓的問題?爲什麼產生消息擠壓,怎麼處理?

解決方案:

這種時候只能操作臨時擴容,以更快的速度去消費數據了。具體操作步驟和思路如下:

①先修復consumer的問題,確保其恢復消費速度,然後將現有consumer都停掉。

②臨時建立好原先10倍或者20倍的queue數量(新建一個topic,partition是原來的10倍)。

③然後寫一個臨時分發消息的consumer程序,這個程序部署上去消費積壓的消息,消費之後不做耗時處理,直接均勻輪詢寫入臨時建好分10數量的queue裏面。

④緊接着徵用10倍的機器來部署consumer,每一批consumer消費一個臨時queue的消息。

⑤這種做法相當於臨時將queue資源和consumer資源擴大10倍,以正常速度的10倍來消費消息。

⑥等快速消費完了之後,恢復原來的部署架構,重新用原來的consumer機器來消費消息。

MQ中消息丟失的三種情況——生產者、消息隊列、消費者

https://img2020.cnblogs.com/blog/1384439/202003/1384439-20200324152227931-2109909666.png

生產者傳輸過程中丟失數據——Confirm機制

所以一般來說,如果你要確保說寫 RabbitMQ 的消息別丟,可以開啓confirm模式,在生產者那裏設置開啓confirm模式之後,你每次寫的消息都會分配一個唯一的 id,然後如果寫入了 RabbitMQ 中,RabbitMQ 會給你回傳一個ack消息,告訴你說這個消息 ok 了。如果 RabbitMQ 沒能處理這個消息,會回調你一個nack接口,告訴你這個消息接收失敗,你可以重試。而且你可以結合這個機制自己在內存裏維護每個消息 id 的狀態,如果超過一定時間還沒接收到這個消息的回調,那麼你可以重發。

消息隊列自身丟失——持久化處理

就是 RabbitMQ 自己弄丟了數據,這個你必須開啓 RabbitMQ 的持久化,就是消息寫入之後會持久化到磁盤,哪怕是 RabbitMQ 自己掛了,恢復之後會自動讀取之前存儲的數據,一般數據不會丟。除非極其罕見的是,RabbitMQ 還沒持久化,自己就掛了,可能導致少量數據丟失,但是這個概率較小。

消費者還未處理就宕機了

這個時候得用 RabbitMQ 提供的ack機制,簡單來說,就是你關閉 RabbitMQ 的自動ack,可以通過一個 api 來調用就行,然後每次你自己代碼裏確保處理完的時候,再在程序裏ack一把。這樣的話,如果你還沒處理完,不就沒有ack?那 RabbitMQ 就認爲你還沒處理完,這個時候 RabbitMQ 會把這個消費分配給別的 consumer 去處理,消息是不會丟的。

https://img2020.cnblogs.com/blog/1384439/202003/1384439-20200324154920126-495907151.png

大量消息在 mq 裏積壓了幾個小時了還沒解決?

一般這個時候,只能臨時緊急擴容了,具體操作步驟和思路如下:

先修復 consumer 的問題,確保其恢復消費速度,然後將現有 consumer 都停掉。新建一個 topic,partition 是原來的 10 倍,臨時建立好原先 10 倍的 queue 數量。

然後寫一個臨時的分發數據的 consumer 程序,這個程序部署上去消費積壓的數據,消費之後不做耗時的處理,直接均勻輪詢寫入臨時建立好的 10 倍數量的 queue。

接着臨時徵用 10倍的機器來部署consumer,每一批 consumer 消費一個臨時 queue 的數據。這種做法相當於是臨時將queue資源和consumer資源擴大10倍,以正常的 10 倍速度來消費數據。等快速消費完積壓數據之後,得恢復原先部署的架構,重新用原先consumer 機器來消費消息。

RabbitMQ中的消息過期失效了(設置過期的時間但是存在消息的大量的丟失)

RabbtiMQ 是可以設置過期時間的,也就是 TTL。如果消息在 queue 中積壓超過一定的時間就會被 RabbitMQ 給清理掉,這個數據就沒了。這就不是說數據會大量積壓在 mq 裏,而是大量的數據會直接搞丟。這個情況下,就不是說要增加 consumer 消費積壓的消息,因爲實際上沒啥積壓,而是丟了大量的消息。我們可以採取一個方案,就是批量重導,這個我們之前線上也有類似的場景幹過。就是大量積壓的時候,我們當時就直接丟棄數據了,然後等過了高峯期以後,將丟失的那批數據,寫個臨時程序,一點一點的查出來,然後重新灌入 mq 裏面去。

爲什麼不應該對所有的 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 進行持久化,需要綜合考慮性能需要,以及可能遇到的問題。

消息堆積的問題:

  消息丟失:

 

 

 有序消費的問題

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章