MQ常見問題及解決方案

https://blog.csdn.net/qq_36236890/article/details/81174504

如何保證消息消費時的冪等性
其實消息重復消費的主要原因在于回饋機制(RabbitMQ是ack,Kafka是offset),在某些場景中我們采用的回饋機制不同,原因也不同,例如消費者消費完消息后回復ack, 但是剛消費完還沒來得及提交系統(tǒng)就重啟了阔蛉,這時候上來就pull消息的時候由于沒有提交ack或者offset,消費的還是上條消息友绝。

那么如何怎么來保證消息消費的冪等性呢屎篱?實際上我們只要保證多條相同的數(shù)據(jù)過來的時候只處理一條或者說多條處理和處理一條造成的結(jié)果相同即可,但是具體怎么做要根據(jù)業(yè)務需求來定啰劲,例如入庫消息梁沧,先查一下消息是否已經(jīng)入庫啊或者說搞個唯一約束啊什么的,還有一些是天生保證冪等性就根本不用去管蝇裤,例如redis就是天然冪等性廷支。

還有一個問題,消費者消費消息的時候在某些場景下要放過消費不了的消息栓辜,遇到消費不了的消息通過日志記錄一下或者搞個什么措施以后再來處理恋拍,但是一定要放過消息,因為在某些場景下例如spring-rabbitmq的默認回饋策略是出現(xiàn)異常就沒有提交ack藕甩,導致了一直在重發(fā)那條消費異常的消息施敢,而且一直還消費不了,這就尷尬了狭莱,后果你會懂的僵娃。

六、如何保證消息的可靠性傳輸腋妙?
由于筆者只使用和實踐過RabbitMQ和Kafka默怨,RocketMQ和ActiveMQ了解的不深,所以分析一下RabbitMQ和Kafka的消息可靠性傳輸?shù)膯栴}骤素。匙睹、

(一)RabbitMQ
(1)生產(chǎn)者弄丟了數(shù)據(jù)
  生產(chǎn)者將數(shù)據(jù)發(fā)送到RabbitMQ的時候,可能數(shù)據(jù)就在半路給搞丟了济竹,因為網(wǎng)絡啥的問題垃僚,都有可能。此時可以選擇用RabbitMQ提供的事務功能规辱,就是生產(chǎn)者發(fā)送數(shù)據(jù)之前開啟RabbitMQ事務(channel.txSelect)谆棺,然后發(fā)送消息,如果消息沒有成功被RabbitMQ接收到,那么生產(chǎn)者會收到異常報錯改淑,此時就可以回滾事務(channel.txRollback)碍岔,然后重試發(fā)送消息;如果收到了消息朵夏,那么可以提交事務(channel.txCommit)蔼啦。但是問題是,RabbitMQ事務機制一搞仰猖,基本上吞吐量會下來捏肢,因為太耗性能。

所以一般來說饥侵,如果你要確保說寫RabbitMQ的消息別丟鸵赫,可以開啟confirm模式,在生產(chǎn)者那里設置開啟confirm模式之后躏升,你每次寫的消息都會分配一個唯一的id辩棒,然后如果寫入了RabbitMQ中,RabbitMQ會給你回傳一個ack消息膨疏,告訴你說這個消息ok了一睁。如果RabbitMQ沒能處理這個消息,會回調(diào)你一個nack接口佃却,告訴你這個消息接收失敗者吁,你可以重試。而且你可以結(jié)合這個機制自己在內(nèi)存里維護每個消息id的狀態(tài)饲帅,如果超過一定時間還沒接收到這個消息的回調(diào)复凳,那么你可以重發(fā)。

事務機制和cnofirm機制最大的不同在于洒闸,事務機制是同步的染坯,你提交一個事務之后會阻塞在那兒,但是confirm機制是異步的丘逸,你發(fā)送個消息之后就可以發(fā)送下一個消息单鹿,然后那個消息RabbitMQ接收了之后會異步回調(diào)你一個接口通知你這個消息接收到了。

所以一般在生產(chǎn)者這塊避免數(shù)據(jù)丟失深纲,都是用confirm機制的仲锄。

(2)RabbitMQ弄丟了數(shù)據(jù)

就是RabbitMQ自己弄丟了數(shù)據(jù),這個你必須開啟RabbitMQ的持久化湃鹊,就是消息寫入之后會持久化到磁盤儒喊,哪怕是RabbitMQ自己掛了,恢復之后會自動讀取之前存儲的數(shù)據(jù)币呵,一般數(shù)據(jù)不會丟怀愧。除非極其罕見的是侨颈,RabbitMQ還沒持久化,自己就掛了芯义,可能導致少量數(shù)據(jù)會丟失的哈垢,但是這個概率較小。

設置持久化有兩個步驟扛拨,第一個是創(chuàng)建queue的時候?qū)⑵湓O置為持久化的耘分,這樣就可以保證RabbitMQ持久化queue的元數(shù)據(jù),但是不會持久化queue里的數(shù)據(jù)绑警;第二個是發(fā)送消息的時候?qū)⑾⒌膁eliveryMode設置為2求泰,就是將消息設置為持久化的,此時RabbitMQ就會將消息持久化到磁盤上去计盒。必須要同時設置這兩個持久化才行渴频,RabbitMQ哪怕是掛了,再次重啟章郁,也會從磁盤上重啟恢復queue枉氮,恢復這個queue里的數(shù)據(jù)志衍。

而且持久化可以跟生產(chǎn)者那邊的confirm機制配合起來暖庄,只有消息被持久化到磁盤之后,才會通知生產(chǎn)者ack了楼肪,所以哪怕是在持久化到磁盤之前培廓,RabbitMQ掛了,數(shù)據(jù)丟了春叫,生產(chǎn)者收不到ack肩钠,你也是可以自己重發(fā)的。

哪怕是你給RabbitMQ開啟了持久化機制暂殖,也有一種可能价匠,就是這個消息寫到了RabbitMQ中,但是還沒來得及持久化到磁盤上呛每,結(jié)果不巧踩窖,此時RabbitMQ掛了,就會導致內(nèi)存里的一點點數(shù)據(jù)會丟失晨横。

(3)消費端弄丟了數(shù)據(jù)

RabbitMQ如果丟失了數(shù)據(jù)洋腮,主要是因為你消費的時候,剛消費到手形,還沒處理啥供,結(jié)果進程掛了,比如重啟了库糠,那么就尷尬了伙狐,RabbitMQ認為你都消費了,這數(shù)據(jù)就丟了。

這個時候得用RabbitMQ提供的ack機制贷屎,簡單來說窒百,就是你關(guān)閉RabbitMQ自動ack,可以通過一個api來調(diào)用就行豫尽,然后每次你自己代碼里確保處理完的時候篙梢,再程序里ack一把。這樣的話美旧,如果你還沒處理完渤滞,不就沒有ack?那RabbitMQ就認為你還沒處理完榴嗅,這個時候RabbitMQ會把這個消費分配給別的consumer去處理妄呕,消息是不會丟的。

(二)Kafka
(1)消費端弄丟了數(shù)據(jù)

唯一可能導致消費者弄丟數(shù)據(jù)的情況嗽测,就是說绪励,你那個消費到了這個消息,然后消費者那邊自動提交了offset唠粥,讓kafka以為你已經(jīng)消費好了這個消息疏魏,其實你剛準備處理這個消息,你還沒處理晤愧,你自己就掛了大莫,此時這條消息就丟咯。

大家都知道kafka會自動提交offset官份,那么只要關(guān)閉自動提交offset只厘,在處理完之后自己手動提交offset,就可以保證數(shù)據(jù)不會丟舅巷。但是此時確實還是會重復消費羔味,比如你剛處理完,還沒提交offset钠右,結(jié)果自己掛了赋元,此時肯定會重復消費一次,自己保證冪等性就好了爬舰。

生產(chǎn)環(huán)境碰到的一個問題们陆,就是說我們的kafka消費者消費到了數(shù)據(jù)之后是寫到一個內(nèi)存的queue里先緩沖一下,結(jié)果有的時候情屹,你剛把消息寫入內(nèi)存queue坪仇,然后消費者會自動提交offset。

然后此時我們重啟了系統(tǒng)垃你,就會導致內(nèi)存queue里還沒來得及處理的數(shù)據(jù)就丟失了

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

這塊比較常見的一個場景椅文,就是kafka某個broker宕機喂很,然后重新選舉partiton的leader時。大家想想皆刺,要是此時其他的follower剛好還有些數(shù)據(jù)沒有同步少辣,結(jié)果此時leader掛了,然后選舉某個follower成leader之后羡蛾,他不就少了一些數(shù)據(jù)漓帅?這就丟了一些數(shù)據(jù)啊。

生產(chǎn)環(huán)境也遇到過痴怨,我們也是忙干,之前kafka的leader機器宕機了,將follower切換為leader之后浪藻,就會發(fā)現(xiàn)說這個數(shù)據(jù)就丟了捐迫。

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

給這個topic設置replication.factor參數(shù):這個值必須大于1,要求每個partition必須有至少2個副本爱葵。
在kafka服務端設置min.insync.replicas參數(shù):這個值必須大于1施戴,這個是要求一個leader至少感知到有至少一個follower還跟自己保持聯(lián)系,沒掉隊萌丈,這樣才能確保leader掛了還有一個follower吧赞哗。
在producer端設置acks=all:這個是要求每條數(shù)據(jù),必須是寫入所有replica之后浓瞪,才能認為是寫成功了懈玻。
在producer端設置retries=MAX(很大很大很大的一個值巧婶,無限次重試的意思):這個是要求一旦寫入失敗乾颁,就無限重試,卡在這里了艺栈。
(3)生產(chǎn)者會不會弄丟數(shù)據(jù)

如果按照上述的思路設置了ack=all英岭,一定不會丟,要求是湿右,你的leader接收到消息诅妹,所有的follower都同步到了消息之后,才認為本次寫成功了毅人。如果沒滿足這個條件吭狡,生產(chǎn)者會自動不斷的重試,重試無限次丈莺。

六划煮、如何保證消息的順序性
因為在某些情況下我們?nèi)舆MMQ中的消息是要嚴格保證順序的,尤其涉及到訂單什么的業(yè)務需求缔俄,消費的時候也是要嚴格保證順序弛秋,不然會出大問題的器躏。

先看看順序會錯亂的倆場景

rabbitmq:一個queue,多個consumer蟹略,這不明顯亂了
kafka:一個topic登失,一個partition,一個consumer挖炬,內(nèi)部多線程揽浙,這不也明顯亂了
  如何來保證消息的順序性呢?
rabbitmq:拆分多個queue意敛,每個queue一個consumer捏萍,就是多一些queue而已,確實是麻煩點空闲;或者就一個queue但是對應一個consumer令杈,然后這個consumer內(nèi)部用內(nèi)存隊列做排隊,然后分發(fā)給底層不同的worker來處理碴倾。
kafka:一個topic逗噩,一個partition,一個consumer跌榔,內(nèi)部單線程消費异雁,寫N個內(nèi)存queue,然后N個線程分別消費一個內(nèi)存queue即可僧须。
七纲刀、如何解決消息隊列的延時以及過期失效問題?消息隊列滿了以后該怎么處理担平?有幾百萬消息持續(xù)積壓幾小時怎么解決示绊?
(一)、大量消息在mq里積壓了幾個小時了還沒解決
幾千萬條數(shù)據(jù)在MQ里積壓了七八個小時暂论,從下午4點多面褐,積壓到了晚上很晚,10點多取胎,11點多
這個是我們真實遇到過的一個場景展哭,確實是線上故障了,這個時候要不然就是修復consumer的問題闻蛀,讓他恢復消費速度匪傍,然后傻傻的等待幾個小時消費完畢。這個肯定不能在面試的時候說吧觉痛。

一個消費者一秒是1000條役衡,一秒3個消費者是3000條,一分鐘是18萬條秧饮,1000多萬條映挂,所以如果你積壓了幾百萬到上千萬的數(shù)據(jù)泽篮,即使消費者恢復了,也需要大概1小時的時間才能恢復過來柑船。

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

先修復consumer的問題鞍时,確保其恢復消費速度亏拉,然后將現(xiàn)有cnosumer都停掉。
新建一個topic逆巍,partition是原來的10倍及塘,臨時建立好原先10倍或者20倍的queue數(shù)量。
然后寫一個臨時的分發(fā)數(shù)據(jù)的consumer程序锐极,這個程序部署上去消費積壓的數(shù)據(jù)笙僚,消費之后不做耗時的處理,直接均勻輪詢寫入臨時建立好的10倍數(shù)量的queue灵再。
接著臨時征用10倍的機器來部署consumer肋层,每一批consumer消費一個臨時queue的數(shù)據(jù)。
這種做法相當于是臨時將queue資源和consumer資源擴大10倍翎迁,以正常的10倍速度來消費數(shù)據(jù)栋猖。
等快速消費完積壓數(shù)據(jù)之后,得恢復原先部署架構(gòu)汪榔,重新用原先的consumer機器來消費消息蒲拉。
(二)、消息隊列過期失效問題
假設你用的是rabbitmq痴腌,rabbitmq是可以設置過期時間的雌团,就是TTL,如果消息在queue中積壓超過一定的時間就會被rabbitmq給清理掉衷掷,這個數(shù)據(jù)就沒了辱姨。那這就是第二個坑了。這就不是說數(shù)據(jù)會大量積壓在mq里戚嗅,而是大量的數(shù)據(jù)會直接搞丟。

這個情況下枢舶,就不是說要增加consumer消費積壓的消息懦胞,因為實際上沒啥積壓,而是丟了大量的消息凉泄。我們可以采取一個方案躏尉,就是批量重導,這個我們之前線上也有類似的場景干過后众。就是大量積壓的時候胀糜,我們當時就直接丟棄數(shù)據(jù)了颅拦,然后等過了高峰期以后,比如大家一起喝咖啡熬夜到晚上12點以后教藻,用戶都睡覺了距帅。

這個時候我們就開始寫程序,將丟失的那批數(shù)據(jù)括堤,寫個臨時程序碌秸,一點一點的查出來,然后重新灌入mq里面去悄窃,把白天丟的數(shù)據(jù)給他補回來讥电。也只能是這樣了。

假設1萬個訂單積壓在mq里面轧抗,沒有處理恩敌,其中1000個訂單都丟了,你只能手動寫程序把那1000個訂單給查出來横媚,手動發(fā)到mq里去再補一次潮剪。

(三)、消息隊列滿了怎么搞分唾?
如果走的方式是消息積壓在mq里抗碰,那么如果你很長時間都沒處理掉,此時導致mq都快寫滿了绽乔,咋辦弧蝇?這個還有別的辦法嗎?沒有折砸,誰讓你第一個方案執(zhí)行的太慢了看疗,你臨時寫程序,接入數(shù)據(jù)來消費睦授,消費一個丟棄一個两芳,都不要了,快速消費掉所有的消息去枷。然后走第二個方案怖辆,到了晚上再補數(shù)據(jù)吧。

來源:CSDN
原文:https://blog.csdn.net/qq_36236890/article/details/81174504
版權(quán)聲明:本文為博主原創(chuàng)文章删顶,轉(zhuǎn)載請附上博文鏈接竖螃!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市逗余,隨后出現(xiàn)的幾起案子特咆,更是在濱河造成了極大的恐慌,老刑警劉巖录粱,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件腻格,死亡現(xiàn)場離奇詭異画拾,居然都是意外死亡,警方通過查閱死者的電腦和手機菜职,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門青抛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人些楣,你說我怎么就攤上這事脂凶。” “怎么了愁茁?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵蚕钦,是天一觀的道長。 經(jīng)常有香客問我鹅很,道長嘶居,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任促煮,我火速辦了婚禮邮屁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘菠齿。我一直安慰自己佑吝,他們只是感情好,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布绳匀。 她就那樣靜靜地躺著芋忿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪疾棵。 梳的紋絲不亂的頭發(fā)上戈钢,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機與錄音是尔,去河邊找鬼殉了。 笑死,一個胖子當著我的面吹牛拟枚,可吹牛的內(nèi)容都是我干的薪铜。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼梨州,長吁一口氣:“原來是場噩夢啊……” “哼痕囱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起暴匠,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎傻粘,沒想到半個月后每窖,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體帮掉,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年窒典,在試婚紗的時候發(fā)現(xiàn)自己被綠了蟆炊。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡瀑志,死狀恐怖涩搓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情劈猪,我是刑警寧澤昧甘,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站战得,受9級特大地震影響充边,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜常侦,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一浇冰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧聋亡,春花似錦肘习、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至致讥,卻和暖如春仅仆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背垢袱。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工墓拜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人请契。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓咳榜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親爽锥。 傳聞我的和親對象是個殘疾皇子涌韩,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

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