主頁 >  其他 > RabbitMQ 高可用之如何確保訊息成功消費

RabbitMQ 高可用之如何確保訊息成功消費

2021-09-09 11:41:55 其他

文章目錄

    • 1. 兩種消費思路
    • 2. 確保消費成功兩種思路
    • 3. 訊息拒絕
    • 4. 訊息確認
      • 4.1 自動確認
      • 4.2 手動確認
        • 4.2.1 推模式手動確認
        • 4.2.2 拉模式手動確認
    • 5. 冪等性問題
    • 6. 小結

前面一篇文章松哥和大家聊了 MQ 高可用之如何確保訊息成功發送,各種配置齊上陣,最終確保了訊息的成功發送,甚至在一些極端情況下還可能發生同一條訊息重復發送的情況,不管怎么樣,訊息總算發送出去了,如果小伙伴們還沒看過上篇文章,建議先看看,再來學習本文:

  • 四種策略確保 RabbitMQ 訊息發送可靠性!你用哪種?

今天我們就來聊一聊訊息消費的問題,看看如何確保訊息消費成功,并且確保冪等性,

1. 兩種消費思路

RabbitMQ 的訊息消費,整體上來說有兩種不同的思路:

  • 推(push):MQ 主動將訊息推送給消費者,這種方式需要消費者設定一個緩沖區去快取訊息,對于消費者而言,記憶體中總是有一堆需要處理的訊息,所以這種方式的效率比較高,這也是目前大多數應用采用的消費方式,
  • 拉(pull):消費者主動從 MQ 拉取訊息,這種方式效率并不是很高,不過有的時候如果服務端需要批量拉取訊息,倒是可以采用這種方式,

兩種方式我都舉個例子看下,

先來看推(push):

這種方式大家比較常見,就是通過 @RabbitListener 注解去標記消費者,如下:

@Component
public class ConsumerDemo {
    @RabbitListener(queues = RabbitConfig.JAVABOY_QUEUE_NAME)
    public void handle(String msg) {
        System.out.println("msg = " + msg);
    }
}

當監聽的佇列中有訊息時,就會觸發該方法,

再來看拉(pull):

@Test
public void test01() throws UnsupportedEncodingException {
    Object o = rabbitTemplate.receiveAndConvert(RabbitConfig.JAVABOY_QUEUE_NAME);
    System.out.println("o = " + new String(((byte[]) o),"UTF-8"));
}

呼叫 receiveAndConvert 方法,方法引數為佇列名稱,方法執行完成后,會從 MQ 上拉取一條訊息下來,如果該方法回傳值為 null,表示該佇列上沒有訊息了,receiveAndConvert 方法有一個多載方法,可以在多載方法中傳入一個等待超時時間,例如 3 秒,此時,假設佇列中沒有訊息了,則 receiveAndConvert 方法會阻塞 3 秒,3 秒內如果佇列中有了新訊息就回傳,3 秒后如果佇列中還是沒有新訊息,就回傳 null,這個等待超時時間要是不設定的話,默認為 0,

這是訊息兩種不同的消費模式,

如果需要從訊息佇列中持續獲得訊息,就可以使用推模式;如果只是單純的消費一條訊息,則使用拉模式即可,切忌將拉模式放到一個死回圈中,變相的訂閱訊息,這會嚴重影響 RabbitMQ 的性能,

2. 確保消費成功兩種思路

在上篇文章中,我們想盡辦法確保訊息能夠發送成功,對于訊息消費成功,其實官方提供了相關的機制,我們一起來看下,

為了保證訊息能夠可靠的到達訊息消費者,RabbitMQ 中提供了訊息消費確認機制,當消費者去消費訊息的時候,可以通過指定 autoAck 引數來表示訊息消費的確認方式,

  • 當 autoAck 為 false 的時候,此時即使消費者已經收到訊息了,RabbitMQ 也不會立馬將訊息移除,而是等待消費者顯式的回復確認信號后,才會將訊息打上洗掉標記,然后再洗掉,
  • 當 autoAck 為 true 的時候,此時訊息消費者就會自動把發送出去的訊息設定為確認,然后將訊息移除(從記憶體或者磁盤中),即使這些訊息并沒有到達消費者,

我們來看一張圖:

如上圖所示,在 RabbitMQ 的 web 管理頁面:

  • Ready 表示待消費的訊息數量,
  • Unacked 表示已經發送給消費者但是還沒收到消費者 ack 的訊息數量,

這是我們可以從 UI 層面觀察訊息的消費情況確認情況,

當我們將 autoAck 設定為 false 的時候,對于 RabbitMQ 而言,消費分成了兩個部分:

  • 待消費的訊息
  • 已經投遞給消費者,但是還沒有被消費者確認的訊息

換句話說,當設定 autoAck 為 false 的時候,消費者就變得非常從容了,它將有足夠的時間去處理這條訊息,當訊息正常處理完成后,再手動 ack,此時 RabbitMQ 才會認為這條訊息消費成功了,如果 RabbitMQ 一直沒有收到客戶端的反饋,并且此時客戶端也已經斷開連接了,那么 RabbitMQ 就會將剛剛的訊息重新放回佇列中,等待下一次被消費,

綜上所述,確保訊息被成功消費,無非就是手動 Ack 或者自動 Ack,無他,當然,無論這兩種中的哪一種,最終都有可能導致訊息被重復消費,所以一般來說我們還需要在處理訊息時,解決冪等性問題,

3. 訊息拒絕

當客戶端收到訊息時,可以選擇消費這條訊息,也可以選擇拒絕這條訊息,我們來看下拒絕的方式:

@Component
public class ConsumerDemo {
    @RabbitListener(queues = RabbitConfig.JAVABOY_QUEUE_NAME)
    public void handle(Channel channel, Message message) {
        //獲取訊息編號
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            //拒絕訊息
            channel.basicReject(deliveryTag, true);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

消費者收到訊息之后,可以選擇拒絕消費該條訊息,拒絕的步驟分兩步:

  1. 獲取訊息編號 deliveryTag,
  2. 呼叫 basicReject 方法拒絕訊息,

呼叫 basicReject 方法時,第二個引數是 requeue,即是否重新入隊,如果第二個引數為 true,則這條被拒絕的訊息會重新進入到訊息佇列中,等待下一次被消費;如果第二個引數為 false,則這條被拒絕的訊息就會被丟掉,不會有新的消費者去消費它了,

需要注意的是,basicReject 方法一次只能拒絕一條訊息,

4. 訊息確認

訊息確認分為自動確認和手動確認,我們分別來看,

4.1 自動確認

先來看看自動確認,在 Spring Boot 中,默認情況下,訊息消費就是自動確認的,

我們來看如下一個訊息消費方法:

@Component
public class ConsumerDemo {
    @RabbitListener(queues = RabbitConfig.JAVABOY_QUEUE_NAME)
    public void handle2(String msg) {
        System.out.println("msg = " + msg);
        int i = 1 / 0;
    }
}

通過 @Componet 注解將當前類注入到 Spring 容器中,然后通過 @RabbitListener 注解來標記一個訊息消費方法,默認情況下,訊息消費方法自帶事務,即如果該方法在執行程序中拋出例外,那么被消費的訊息會重新回到佇列中等待下一次被消費,如果該方法正常執行完沒有拋出例外,則這條訊息就算是被消費了,

4.2 手動確認

手動確認我又把它分為兩種:推模式手動確認與拉模式手動確認,

4.2.1 推模式手動確認

要開啟手動確認,需要我們首先關閉自動確認,關閉方式如下:

spring.rabbitmq.listener.simple.acknowledge-mode=manual

這個配置表示將訊息的確認模式改為手動確認,

接下來我們來看下消費者中的代碼:

@RabbitListener(queues = RabbitConfig.JAVABOY_QUEUE_NAME)
public void handle3(Message message,Channel channel) {
    long deliveryTag = message.getMessageProperties().getDeliveryTag();
    try {
        //訊息消費的代碼寫到這里
        String s = new String(message.getBody());
        System.out.println("s = " + s);
        //消費完成后,手動 ack
        channel.basicAck(deliveryTag, false);
    } catch (Exception e) {
        //手動 nack
        try {
            channel.basicNack(deliveryTag, false, true);
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

將消費者要做的事情放到一個 try..catch 代碼塊中,

如果訊息正常消費成功,則執行 basicAck 完成確認,

如果訊息消費失敗,則執行 basicNack 方法,告訴 RabbitMQ 訊息消費失敗,

這里涉及到兩個方法:

  • basicAck:這個是手動確認訊息已經成功消費,該方法有兩個引數:第一個引數表示訊息的 id;第二個引數 multiple 如果為 false,表示僅確認當前訊息消費成功,如果為 true,則表示當前訊息之前所有未被當前消費者確認的訊息都消費成功,
  • basicNack:這個是告訴 RabbitMQ 當前訊息未被成功消費,該方法有三個引數:第一個引數表示訊息的 id;第二個引數 multiple 如果為 false,表示僅拒絕當前訊息的消費,如果為 true,則表示拒絕當前訊息之前所有未被當前消費者確認的訊息;第三個引數 requeue 含義和前面所說的一樣,被拒絕的訊息是否重新入隊,

當 basicNack 中最后一個引數設定為 false 的時候,還涉及到一個死信佇列的問題,這個松哥以后再專門寫文章和大家細聊,

4.2.2 拉模式手動確認

拉模式手動 ack 比較麻煩一些,在 Spring 中封裝的 RabbitTemplate 中并未找到對應的方法,所以我們得用原生的辦法,如下:

public void receive2() {
    Channel channel = rabbitTemplate.getConnectionFactory().createConnection().createChannel(true);
    long deliveryTag = 0L;
    try {
        GetResponse getResponse = channel.basicGet(RabbitConfig.JAVABOY_QUEUE_NAME, false);
        deliveryTag = getResponse.getEnvelope().getDeliveryTag();
        System.out.println("o = " + new String((getResponse.getBody()), "UTF-8"));
        channel.basicAck(deliveryTag, false);
    } catch (IOException e) {
        try {
            channel.basicNack(deliveryTag, false, true);
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

這里涉及到的 basicAck 和 basicNack 方法跟前面的一樣,我就不再贅述,

5. 冪等性問題

最后我們再來說說訊息的冪等性問題,

大家設想下面一個場景:

消費者在消費完一條訊息后,向 RabbitMQ 發送一個 ack 確認,此時由于網路斷開或者其他原因導致 RabbitMQ 并沒有收到這個 ack,那么此時 RabbitMQ 并不會將該條訊息洗掉,當重新建立起連接后,消費者還是會再次收到該條訊息,這就造成了訊息的重復消費,同時,由于類似的原因,訊息在發送的時候,同一條訊息也可能會發送兩次(參見四種策略確保 RabbitMQ 訊息發送可靠性!你用哪種?),種種原因導致我們在消費訊息時,一定要處理好冪等性問題,

冪等性問題的處理倒也不難,基本上都是從業務上來處理,我來大概說說思路,

采用 Redis,在消費者消費訊息之前,現將訊息的 id 放到 Redis 中,存盤方式如下:

  • id-0(正在執行業務)
  • id-1(執行業務成功)

如果 ack 失敗,在 RabbitMQ 將訊息交給其他的消費者時,先執行 setnx,如果 key 已經存在(說明之前有人消費過該訊息),獲取他的值,如果是 0,當前消費者就什么都不做,如果是 1,直接 ack,

極端情況:第一個消費者在執行業務時,出現了死鎖,在 setnx 的基礎上,再給 key 設定一個生存時間,生產者,發送訊息時,指定 messageId,

當然這只是一個簡單思路供大家參考,

松哥在 vhr 專案中也處理了訊息冪等性問題,感興趣的小伙伴可以查看 vhr 原始碼(https://github.com/lenve/vhr),代碼在 mailserver 中,

6. 小結

好啦,今天就和小伙伴們聊了下 RabbitMQ 中和訊息消費相關的幾個話題,感興趣的小伙伴可以實踐下哦~

復制文章標題并在公眾號后臺回復,可以下載本文案例~

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/298625.html

標籤:其他

上一篇:Java學習路線總結(全網最強,建議收藏)

下一篇:3.1.4、MySQL__資料庫分組,拼接查詢,日期函式,日期加減,間隔,數值四舍五入,排序,分組,having篩選,分組TopN,流程控制函式,

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more