亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何保障消息中間件不丟失

發布時間:2021-06-23 15:02:35 來源:億速云 閱讀:276 作者:chen 欄目:編程語言

這篇文章主要介紹“如何保障消息中間件不丟失”,在日常操作中,相信很多人在如何保障消息中間件不丟失問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”如何保障消息中間件不丟失”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

前言

RabbitMQ,RocketMQ,Kafka 等。引入中間件的好處可以起到抗高并發,削峰,業務解耦的作用。

如何保障消息中間件不丟失

如上圖

  1. 訂單服務投遞消息給MQ中間件

  2. 庫存服務監聽MQ中間件消息,從而進行消費

之前公司在業務搭建的時候用的就是這種MQ解耦機制,那么如何保障將訂單服務的消息成功投遞給MQ中間件,保證消息的可靠性?

問題

可能會有人疑問,訂單服務發起消息,返回成功不就OK了嗎?下面有一個Demo代碼

如何保障消息中間件不丟失

一般的發送消息都是這么寫的,但是有一個場景必須在業務搭建之初就要考慮。如果MQ服務器突然宕機了呢?我們發送的消息是不是就沒有了呢?

是的! 一般MQ中間件為了保證提供系統的吞吐量會把消息保存在內存中,如果不作其他處理,MQ一旦宕機,消息就會全部丟失。這個在業務中是絕對不允許的,造成的影響是非常大的!

那么如何解決這個問題?

消息持久化

MQ中發消息的時候會有一個durable參數可以設置,設置為true,就會持久化!

如何保障消息中間件不丟失

這樣的話MQ服務器及時宕機,重啟后磁盤文件中有消息的存儲,這樣就不會丟失了,但是這種方式也僅僅只是有概率的消息不丟失。

如果消息剛剛保存到MQ內存中,還沒來得及更新到磁盤中,突然宕機了(一般高并發情況下發生幾率會很高),尤其是大量消息投遞的過程中。

如何才能做到一定持久化到磁盤中呢?

confirm機制

上面的問題主要在于,沒有人告訴我們持久化是否成功。好在MQ有回調通知特性,confirm機制來通知我們是否持久化成功。

如何保障消息中間件不丟失

confirm機制原理

  • 消息生產者把消息發送給MQ,如果接收成功,MQ會返回一個ack消息- 給生產者。

  • 如果消息不成功,MQ會返回一個nack消息給生產者。

如何保障消息中間件不丟失

上面的demo代碼中有兩個確認機制,一個ACK回調,一個NACK回調。

這樣是不是就可以100%確保消息不丟失了呢?

吞吐量問題嚴重

試想一下,如果我們生產者每發一條消息,都要 MQ 持久化到磁盤中,然后再發起 ack 或 nack 的回調。這樣的話是不是我們 MQ 的吞吐量很不高,因為每次都要把消息持久化到磁盤中。寫入磁盤這個動作是很慢的。這個在高并發場景下是不能夠接受的,吞吐量太低了。

所以 MQ 持久化磁盤真實的實現,是通過異步調用處理的,他是有一定的機制,如:等到有幾千條消息的時候,會一次性的刷盤到磁盤上面。而不是每來一條消息,就刷盤一次。

所以 comfirm 機制其實是一個異步監聽的機制,是為了保證系統的高吞吐量,這樣就導致了還是不能夠 100%保障消息不丟失,因為即使加上了 confirm 機制,消息在 MQ 內存中還沒有刷盤到磁盤就宕機了,還是沒法處理。

消息提前持久化 + 定時任務

其實本質的原因是無法確定是否持久化。

ps:圖畫的有點辣雞~~~~

如何保障消息中間件不丟失

流程操作

  1. 訂單服務生產者再投遞消息之前,先把消息持久化到 Redis 或 DB 中,建議 redis,高性能。消息的狀態為發送中

  2. confirm 機制監聽消息是否發送成功?如 ack 成功消息,刪除 redis 中此消息。

  3. 如果 nack 不成功的消息,這個可以根據自身的業務選擇是否重發此消息。也可以刪除此消息,由自己的業務決定。

  4. 這邊加了個定時任務,來拉取隔一定時間了,消息狀態還是為發送中的,這個狀態就表明,訂單服務是沒有收到 ack 成功消息。

  5. 定時任務會作補償性的投遞消息。這個時候如果 MQ 回調 ack 成功接收了,再把 redis 中此消息刪除。

補償機制方案

這樣的機制其實就是一個補償機制,我不管 MQ 有沒有真正的接收到,只要我的 redis 中的消息狀態也是為==發送中==,就表示此消息沒有正確成功投遞。再啟動定時任務去監控,發起補償投遞。

機制的優化

當然定時任務那邊我們還可以加上一個補償的次數,如果大于 3 次,還是沒有收到 ack 消息,那就直接把消息的狀態設置為【失敗】,由人工去排查到底是為什么?

這樣的話方案就比較完美了,保障了 100%的消息不丟失、磁盤要是壞了,那就沒法保障了,就要考慮集群方案。

方案問題

不過這樣的方案,就會有可能發送多次相同的消息,很有可能 MQ 已經收到了消息,就是 ack 消息回調時出現網絡故障,沒有讓生產者收到。那就要要求消費者一定在消費的時候保障冪等性

冪等含義

我們先了解一下什么叫冪等?在分布式應用中,冪等是非常重要的,也就是相同條件下對一個業務的操作,不管操作多少次,結果都是一樣。

分布式冪等

為什么要有冪等這種場景?因為在大的系統中,都是分布式部署,如:訂單業務庫存業務 有可能都是獨立部署的,都是單獨的服務。用戶下訂單,會調用到訂單服務和庫存服務。

分布式異常問題

因為分布式部署,很有可能在調用庫存服務時,因為網絡等原因,訂單服務調用失敗,但其實庫存服務已經處理完成。只是返回給訂單服務處理結果時出現了異常。這個時候一般系統會作補償方案,也就是訂單服務再此放起庫存服務的調用,庫存減 1。

update m_goods set count = count - 1 where g_id=10

這樣就出現了問題,其實上一次調用已經減了 1,只是訂單服務沒有收到處理結果。現在又調用一次,又要減 1,這樣就不符合業務了,多扣了。

冪等這個概念就是,不管庫存服務在相同條件下調用幾次,處理結果都一樣。這樣才能保證補償方案的可行性。

樂觀鎖方案

借鑒網上的樂觀鎖方案,例如:

update m_goods set count = count -1 , version = version + 1 where g_id=2 and version = 1

根據 version 版本,也就是在操作庫存前先獲取當前商品的 version 版本號,然后操作的時候帶上此 version 號。我們梳理下,我們第一次操作庫存時,得到 version 結果如下:

  1. 調用庫存服務 version 變成了 2;但返回給訂單服務出現了問題,訂單服務又一次發起調用庫存服務,當訂單服務傳如的 version 還是 1,再執行上面的 sql 語句時,就不會執行;因為 version 已經變為 2 了,where 條件就不成立。這樣就保證了不管調用幾次,只會真正的處理一次。

唯一 ID + 指紋碼

此方案是網上找到的一篇博客所寫的,感覺不錯。

原理就是利用數據庫主鍵去重,業務完成后插入主鍵標識~

select count(1) from t_check where ID = 唯一ID + 指紋碼
  • 唯一 ID 就是業務表的唯一的主鍵,如商品 ID

  • 指紋碼就是為了區別每次正常操作的碼,每次操作時生成指紋碼;可以用時間戳+業務編號的方式。

上面的 sql 語句:

  • 返回如果為 0 表示沒有操作過,那業務操作后就可以 insert into t_check(唯一 ID+指紋碼)

  • 返回如果大于 0 表示操作過,就直接返回

好處:實現簡單

壞處:高并發下數據庫瓶頸

解決方案:根據 ID 進行分庫分表進行算法路由

redis 原子操作(推薦使用)

利用 redis 的原子操作,做個操作完成的標記。這個性能就比較好。但會遇到一些問題。

問題:我們是否需要把業務結果進行數據落庫,如果落庫,關鍵解決的問題時數據庫和 redis 操作如何做到原子性?

這個意思就是庫存減 1 了,但 redis 進行操作完成標記時,失敗了怎么辦?也就是一定要保證落庫和 redis 要么一起成功,要么一起失敗

第二:如果不進行落庫,那么都存儲到緩存中,如何設置定時同步策略?

這個意思就是庫存減 1,不落庫,直接先操作 redis 操作完成標記,然后由另外的同步服務進行庫存落庫,這個就是增加了系統復雜性,而且同步策略的設置。

到此,關于“如何保障消息中間件不丟失”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

乃东县| 平乡县| 兴仁县| 涞源县| 永川市| 平塘县| 洪雅县| 团风县| 凉城县| 观塘区| 崇左市| 巴林左旗| 咸阳市| 石首市| 延吉市| 汤原县| 雷山县| 永清县| 阿瓦提县| 聊城市| 博乐市| 广丰县| 县级市| 西乌珠穆沁旗| 江山市| 肇东市| 新余市| 金华市| 桦南县| 陕西省| 内黄县| 白山市| 阿勒泰市| 吉隆县| 即墨市| 勃利县| 东乡族自治县| 北川| 濮阳市| 海城市| 淮南市|