mq如何保證高可用葬毫,解決重復(fù)消費(fèi)镇辉、數(shù)據(jù)丟失問題和順序性問題

一、如何保證消息隊(duì)列的高可用

1. RabbitMQ的高可用性

rabbitmq有三種模式:單機(jī)模式贴捡,普通集群模式忽肛,鏡像集群模式

  • 普通集群模式:多臺機(jī)器部署,每個(gè)機(jī)器放一個(gè)rabbitmq實(shí)例烂斋,但是創(chuàng)建的queue只會放在一個(gè)rabbitmq實(shí)例上屹逛,每個(gè)實(shí)例同步queue的元數(shù)據(jù)。如果消費(fèi)時(shí)連的是其他實(shí)例汛骂,那個(gè)實(shí)例會從queue所在實(shí)例拉取數(shù)據(jù)罕模。這就會導(dǎo)致拉取數(shù)據(jù)的開銷,如果那個(gè)放queue的實(shí)例宕機(jī)了帘瞭,那么其他實(shí)例就無法從那個(gè)實(shí)例拉取淑掌,即便開啟了消息持久化,讓rabbitmq落地存儲消息的話蝶念,消息不一定會丟抛腕,但得等這個(gè)實(shí)例恢復(fù)了,然后才可以繼續(xù)從這個(gè)queue拉取數(shù)據(jù)媒殉,這就沒什么高可用可言担敌,主要是提供吞吐量,讓集群中多個(gè)節(jié)點(diǎn)來服務(wù)某個(gè)queue的讀寫操作廷蓉。
  • 鏡像集群模式:queue的元數(shù)據(jù)和消息都會存放在多個(gè)實(shí)例全封,每次寫消息就自動同步到多個(gè)queue實(shí)例里。這樣任何一個(gè)機(jī)器宕機(jī),其他機(jī)器都可以頂上刹悴,但是性能開銷太大给猾,消息同步導(dǎo)致網(wǎng)絡(luò)帶寬壓力和消耗很重,另外颂跨,沒有擴(kuò)展性可言敢伸,如果queue負(fù)載很重,加機(jī)器恒削,新增的機(jī)器也包含了這個(gè)queue的所有數(shù)據(jù)池颈,并沒有辦法線性擴(kuò)展你的queue。此時(shí)钓丰,需要開啟鏡像集群模式躯砰,在rabbit管理控制臺新增一個(gè)策略,將數(shù)據(jù)同步到指定數(shù)量的節(jié)點(diǎn)携丁,然后你再次創(chuàng)建queue的時(shí)候琢歇,應(yīng)用這個(gè)策略,就會自動將數(shù)據(jù)同步到其他的節(jié)點(diǎn)上去了
2. kafka的高可用性

kafka架構(gòu):多個(gè)broker組成梦鉴,每個(gè)broker是一個(gè)節(jié)點(diǎn)李茫;創(chuàng)建一個(gè)topic,這個(gè)topic可以劃分為多個(gè)partition肥橙,每個(gè)partition可以存在于不同的broker上魄宏,每個(gè)partition就放一部分?jǐn)?shù)據(jù)。

它是一個(gè)分布式消息隊(duì)列存筏,就是說一個(gè)topic的數(shù)據(jù)宠互,是分散放在多個(gè)機(jī)器上的,每個(gè)機(jī)器就放一部分?jǐn)?shù)據(jù)椭坚。


kafka 0.8以前予跌,是沒有HA機(jī)制的,就是任何一個(gè)broker宕機(jī)了善茎,那個(gè)broker上的partition就廢了券册,沒法寫也沒法讀,沒有什么高可用性可言巾表。


kafka 0.8以后汁掠,提供了HA機(jī)制略吨,就是replica副本機(jī)制集币。每個(gè)partition的數(shù)據(jù)都會同步到吉他機(jī)器上,形成自己的多個(gè)replica副本翠忠。然后所有replica會選舉一個(gè)leader出來鞠苟,那么生產(chǎn)和消費(fèi)都跟這個(gè)leader打交道,然后其他replica就是follower。寫的時(shí)候当娱,leader會負(fù)責(zé)把數(shù)據(jù)同步到所有follower上去吃既,讀的時(shí)候就直接讀leader上數(shù)據(jù)即可。kafka會均勻的將一個(gè)partition的所有replica分布在不同的機(jī)器上跨细,從而提高容錯(cuò)性鹦倚。

如果某個(gè)broker宕機(jī)了也沒事,它上面的partition在其他機(jī)器上都有副本的冀惭,如果這上面有某個(gè)partition的leader震叙,那么此時(shí)會重新選舉一個(gè)新的leader出來,大家繼續(xù)讀寫那個(gè)新的leader即可散休。這就有所謂的高可用性了媒楼。


寫數(shù)據(jù)的時(shí)候,生產(chǎn)者就寫leader戚丸,然后leader將數(shù)據(jù)落地寫本地磁盤划址,接著其他follower自己主動從leader來pull數(shù)據(jù)。一旦所有follower同步好數(shù)據(jù)了限府,就會發(fā)送ack給leader夺颤,leader收到所有follower的ack之后,就會返回寫成功的消息給生產(chǎn)者胁勺。

消費(fèi)的時(shí)候拂共,只會從leader去讀,但是只有當(dāng)消息已經(jīng)被所有follower都同步成功返回ack的時(shí)候姻几,這個(gè)消息才會被消費(fèi)者讀到宜狐。

二、如何保證消息不被重復(fù)消費(fèi)

kafka重復(fù)消費(fèi)的情況:
kafka有個(gè)offset的概念蛇捌,就是每個(gè)消息寫進(jìn)去抚恒,都有一個(gè)offset,代表他的序號络拌,然后consumer消費(fèi)了數(shù)據(jù)之后俭驮,每隔一段時(shí)間,會把自己消費(fèi)過的消息的offset提交一下春贸,下次重啟時(shí)混萝,從上次消費(fèi)到的offset來繼續(xù)消費(fèi)。但是offset沒來得及提交就重啟萍恕,這部分會再次消費(fèi)一次逸嘀。


怎么保證消息隊(duì)列消費(fèi)的冪等性:

  • 比如數(shù)據(jù)寫庫,可以先根據(jù)主鍵查一下允粤,如果這數(shù)據(jù)都有了崭倘,就update
  • 比如寫redis翼岁,那沒問題,因?yàn)槊看味际莝et司光,天然冪等性
  • 如果不是上面兩個(gè)場景琅坡,那做的稍微復(fù)雜一點(diǎn),需要讓生產(chǎn)者發(fā)送每條數(shù)據(jù)的時(shí)候残家,里面加一個(gè)全局唯一的id榆俺,類似訂單id之類的東西,然后消費(fèi)到了后坞淮,先根據(jù)這個(gè)id去比如redis里查一下谴仙,之前消費(fèi)過嗎?如果沒有消費(fèi)過碾盐,就處理晃跺,然后這個(gè)id寫redis。如果消費(fèi)過了毫玖,那就別處理了掀虎,保證別重復(fù)處理相同的消息即可。
  • 還有比如基于數(shù)據(jù)庫的唯一鍵來保證重復(fù)數(shù)據(jù)不會重復(fù)插入多條付枫,重復(fù)數(shù)據(jù)拿到了以后我們插入的時(shí)候烹玉,因?yàn)橛形ㄒ绘I約束了,所以重復(fù)數(shù)據(jù)只會插入報(bào)錯(cuò)阐滩,不會導(dǎo)致數(shù)據(jù)庫中出現(xiàn)臟數(shù)據(jù)

三侠碧、如何保證消息的可靠性傳輸(如何處理消息丟失的問題)

丟數(shù)據(jù)谷饿,mq一般分為兩種,要么是mq自己弄丟了,要么是我們消費(fèi)的時(shí)候弄丟了

1. rabbitmq
  • 生產(chǎn)者弄丟了數(shù)據(jù):


    生產(chǎn)者將數(shù)據(jù)發(fā)送到rabbitmq的時(shí)候踱卵,因?yàn)榫W(wǎng)絡(luò)或者其他問題僻肖,半路給搞丟了篙梢。


    此時(shí)可以選擇用rabbitmq提供的事務(wù)功能光羞,就是生產(chǎn)者發(fā)送數(shù)據(jù)之前開啟rabbitmq事務(wù)(channel.txSelect),然后發(fā)送消息穴豫,如果消息沒有成功被rabbitmq接收到凡简,那么生產(chǎn)者會收到異常報(bào)錯(cuò),此時(shí)就可以回滾事務(wù)(channel.txRollback)精肃,然后重試發(fā)送消息秤涩;如果收到了消息,那么可以提交事務(wù)(channel.txCommit)司抱。但是rabbitmq事務(wù)機(jī)會降低吞吐量筐眷,因?yàn)樘男阅堋?br>

    所以一般是開啟confirm模式,在生產(chǎn)者那里設(shè)置開啟confirm模式后状植,每次寫消息都會分配一個(gè)唯一的id浊竟,然后如果寫入了rabbitmq中怨喘,rabbitmq會回傳一個(gè)ack消息津畸,告訴你說這個(gè)消息ok了振定。如果rabbitmq沒能處理這個(gè)消息,會回調(diào)你一個(gè)nack接口肉拓,告訴你這個(gè)消息接收失敗后频,你可以重試。而且你可以結(jié)合這個(gè)機(jī)制自己在內(nèi)存里維護(hù)每個(gè)消息id的狀態(tài)暖途,如果超過一定時(shí)間還沒接收到這個(gè)消息的回調(diào)卑惜,那么你可以重發(fā)


    事務(wù)機(jī)制和cnofirm機(jī)制最大的不同在于,事務(wù)機(jī)制是同步的驻售,你提交一個(gè)事務(wù)之后會阻塞在那兒露久,但是confirm機(jī)制是異步的,你發(fā)送個(gè)消息之后就可以發(fā)送下一個(gè)消息欺栗,然后那個(gè)消息rabbitmq接收了之后會異步回調(diào)你一個(gè)接口通知你這個(gè)消息接收到了毫痕。

  • rabbitmq丟失數(shù)據(jù):

    開啟rabbitmq的持久化。設(shè)置持久化有兩個(gè)步驟迟几,第一個(gè)是創(chuàng)建queue的時(shí)候?qū)⑵湓O(shè)置為持久化的消请,這樣就可以保證rabbitmq持久化queue的元數(shù)據(jù),但是不會持久化queue里的數(shù)據(jù)类腮;第二個(gè)是發(fā)送消息的時(shí)候?qū)⑾⒌膁eliveryMode設(shè)置為2臊泰,就是將消息設(shè)置為持久化的,此時(shí)rabbitmq就會將消息持久化到磁盤上去蚜枢。必須要同時(shí)設(shè)置這兩個(gè)持久化才行缸逃,rabbitmq哪怕是掛了,再次重啟厂抽,也會從磁盤上重啟恢復(fù)queue察滑,恢復(fù)這個(gè)queue里的數(shù)據(jù)。

    而且持久化可以跟生產(chǎn)者那邊的confirm機(jī)制配合起來修肠,只有消息被持久化到磁盤之后贺辰,才會通知生產(chǎn)者ack了,所以哪怕是在持久化到磁盤之前嵌施,rabbitmq掛了饲化,數(shù)據(jù)丟了,生產(chǎn)者收不到ack吗伤,你也是可以自己重發(fā)的吃靠。

  • 消費(fèi)端弄丟了數(shù)據(jù)


    消費(fèi)的時(shí)候,剛消費(fèi)到足淆,還沒處理巢块,結(jié)果進(jìn)程掛了礁阁,比如重啟,此時(shí)rabbitmq認(rèn)為消費(fèi)過了族奢,這數(shù)據(jù)就丟了姥闭。


    用rabbitmq提供的ack機(jī)制,簡單來說越走,就是關(guān)閉rabbitmq自動ack棚品,可以通過一個(gè)api來調(diào)用,然后每次代碼里確保處理完的時(shí)候廊敌,再程序里ack一把铜跑。這樣的話,如果還沒處理完骡澈,就沒有ack锅纺,那rabbitmq就認(rèn)為你還沒處理完,這個(gè)時(shí)候rabbitmq會把這個(gè)消費(fèi)分配給別的consumer去處理肋殴,消息是不會丟的囤锉。
2. kafka
  • 消費(fèi)端弄丟了數(shù)據(jù)


    唯一可能就是消費(fèi)到了這個(gè)消息,然后消費(fèi)者那邊自動提交了offset疼电,讓kafka以為你已經(jīng)消費(fèi)好了這個(gè)消息嚼锄,其實(shí)你剛準(zhǔn)備處理這個(gè)消息,你還沒處理蔽豺,你自己就掛了区丑,此時(shí)這條消息就丟了。


    只要關(guān)閉自動提交offset修陡,在處理完之后自己手動提交offset沧侥,就可以保證數(shù)據(jù)不會丟。但是此時(shí)確實(shí)還是會重復(fù)消費(fèi)魄鸦,比如剛處理完宴杀,還沒提交offset,結(jié)果自己掛了拾因,此時(shí)肯定會重復(fù)消費(fèi)一次旺罢,自己保證冪等性就好了。

  • kafka弄丟了數(shù)據(jù)


    kafka某個(gè)broker宕機(jī)绢记,然后重新選舉partiton的leader時(shí)扁达。大家想想,要是此時(shí)其他的follower剛好還有些數(shù)據(jù)沒有同步蠢熄,結(jié)果此時(shí)leader掛了跪解,然后選舉某個(gè)follower成leader之后,中間這部分?jǐn)?shù)據(jù)就丟失了签孔。


    所以此時(shí)一般是要求起碼設(shè)置如下4個(gè)參數(shù):

    (1)給這個(gè)topic設(shè)置replication.factor參數(shù):這個(gè)值必須大于1叉讥,要求每個(gè)partition必須有至少2個(gè)副本

    (2)在kafka服務(wù)端設(shè)置min.insync.replicas參數(shù):這個(gè)值必須大于1窘行,這個(gè)是要求一個(gè)leader至少感知到有至少一個(gè)follower還跟自己保持聯(lián)系,沒掉隊(duì)图仓,這樣才能確保leader掛了還有一個(gè)follower

    (3)在producer端設(shè)置acks=all:這個(gè)是要求每條數(shù)據(jù)罐盔,必須是寫入所有replica之后,才能認(rèn)為是寫成功了

    (4)在producer端設(shè)置retries=MAX(很大很大很大的一個(gè)值透绩,無限次重試的意思):這個(gè)是要求一旦寫入失敗翘骂,就無限重試壁熄,卡在這里了

  • 生產(chǎn)者會不會弄丟數(shù)據(jù)


    如果按照上述的思路設(shè)置了ack=all帚豪,一定不會丟,要求是草丧,你的leader接收到消息狸臣,所有的follower都同步到了消息之后,才認(rèn)為本次寫成功了昌执。如果沒滿足這個(gè)條件烛亦,生產(chǎn)者會自動不斷的重試,重試無限次懂拾。

三煤禽、如何保證消息的順序性

1. rabbitmq

拆分多個(gè)queue,每個(gè)queue一個(gè)consumer岖赋,就是多一些queue而已檬果,確實(shí)是麻煩點(diǎn);或者就一個(gè)queue但是對應(yīng)一個(gè)consumer唐断,然后這個(gè)consumer內(nèi)部用內(nèi)存隊(duì)列做排隊(duì)选脊,然后分發(fā)給底層不同的worker來處理

2. kafka

寫入一個(gè)partition中的數(shù)據(jù)一定是有序的,生產(chǎn)者在寫的時(shí)候 脸甘,可以指定一個(gè)key恳啥,比如指定訂單id作為key,這個(gè)訂單相關(guān)數(shù)據(jù)一定會被分發(fā)到一個(gè)partition中去丹诀。消費(fèi)者從partition中取出數(shù)據(jù)的時(shí)候也一定是有序的钝的,把每個(gè)數(shù)據(jù)放入對應(yīng)的一個(gè)內(nèi)存隊(duì)列,一個(gè)partition中有幾條相關(guān)數(shù)據(jù)就用幾個(gè)內(nèi)存隊(duì)列铆遭,消費(fèi)者開啟多個(gè)線程硝桩,每個(gè)線程處理一個(gè)內(nèi)存隊(duì)列。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疚脐,一起剝皮案震驚了整個(gè)濱河市亿柑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌棍弄,老刑警劉巖望薄,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疟游,死亡現(xiàn)場離奇詭異,居然都是意外死亡痕支,警方通過查閱死者的電腦和手機(jī)颁虐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來卧须,“玉大人另绩,你說我怎么就攤上這事』ㄋ唬” “怎么了笋籽?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長椭员。 經(jīng)常有香客問我车海,道長,這世上最難降的妖魔是什么隘击? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任侍芝,我火速辦了婚禮,結(jié)果婚禮上埋同,老公的妹妹穿的比我還像新娘州叠。我一直安慰自己,他們只是感情好凶赁,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布咧栗。 她就那樣靜靜地躺著,像睡著了一般哟冬。 火紅的嫁衣襯著肌膚如雪楼熄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天浩峡,我揣著相機(jī)與錄音可岂,去河邊找鬼。 笑死翰灾,一個(gè)胖子當(dāng)著我的面吹牛缕粹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播纸淮,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼平斩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了咽块?” 一聲冷哼從身側(cè)響起绘面,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后揭璃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體晚凿,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年瘦馍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了歼秽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,722評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡情组,死狀恐怖燥筷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情院崇,我是刑警寧澤肆氓,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站亚脆,受9級特大地震影響做院,放射性物質(zhì)發(fā)生泄漏盲泛。R本人自食惡果不足惜濒持,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望寺滚。 院中可真熱鬧柑营,春花似錦、人聲如沸村视。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蚁孔。三九已至奶赔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間杠氢,已是汗流浹背站刑。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鼻百,地道東北人绞旅。 一個(gè)月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像温艇,于是被迫代替她去往敵國和親因悲。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容