每次都碰到面試官問(wèn)我如何保證Kafka不丟失消息,快哭了
一、背景引入
這篇文章,我們來(lái)聊聊在線上生產(chǎn)環(huán)境使用消息中間件技術(shù)的時(shí)候,從前到后的全鏈路到底如何保證數(shù)據(jù)不能丟失。
這個(gè)問(wèn)題,在互聯(lián)網(wǎng)公司面試的時(shí)候高頻出現(xiàn),而且也是非?,F(xiàn)實(shí)的生產(chǎn)環(huán)境問(wèn)題。
如果你的簡(jiǎn)歷中寫了自己熟悉MQ技術(shù)(RabbitMQ、RocketMQ、Kafka),而且在項(xiàng)目里有使用的經(jīng)驗(yàn),那么非常實(shí)際的一個(gè)生產(chǎn)環(huán)境問(wèn)題就是:投遞消息到MQ,然后從MQ消費(fèi)消息來(lái)處理的這個(gè)過(guò)程,數(shù)據(jù)到底會(huì)不會(huì)丟失。
面試官此時(shí)會(huì)問(wèn):如果數(shù)據(jù)會(huì)丟失的話,你們項(xiàng)目生產(chǎn)部署的時(shí)候,是通過(guò)什么手段保證基于MQ傳輸?shù)臄?shù)據(jù)100%不會(huì)丟失的?麻煩結(jié)合你們線上使用的消息中間件來(lái)具體說(shuō)說(shuō)你們的技術(shù)方案。
這個(gè)其實(shí)就是非常區(qū)分面試候選人技術(shù)水平的一個(gè)問(wèn)題。
實(shí)際上相當(dāng)大比例的普通工程師,哪怕是在一些中小型互聯(lián)網(wǎng)公司里工作過(guò)的,也就是基于公司部署的MQ集群簡(jiǎn)單的使用一下罷了,可能代碼層面就是基本的發(fā)送消息和消費(fèi)消息,基本沒(méi)考慮太多的技術(shù)方案。
但是實(shí)際上,對(duì)于MQ、緩存、分庫(kù)分表、NoSQL等各式各類的技術(shù)以及中間件在使用的時(shí)候,都會(huì)有對(duì)應(yīng)技術(shù)相關(guān)的一堆生產(chǎn)環(huán)境問(wèn)題。
那么針對(duì)這些問(wèn)題,就必須要有相對(duì)應(yīng)的一整套技術(shù)方案來(lái)保證系統(tǒng)的健壯性、穩(wěn)定性以及高可用性。
所以其實(shí)中大型互聯(lián)網(wǎng)公司的面試官在面試候選人的時(shí)候,如果考察對(duì)MQ相關(guān)技術(shù)的經(jīng)驗(yàn)和掌握程度,十有八九都會(huì)拋出這個(gè)使用MQ時(shí)一定會(huì)涉及的數(shù)據(jù)丟失問(wèn)題。因?yàn)檫@個(gè)問(wèn)題,能夠非常好的區(qū)分候選人的技術(shù)水平。
所以這篇文章,我們就來(lái)具體聊聊基于RabbitMQ這種消息中間件的背景下,從投遞消息到MQ,到從MQ消費(fèi)消息出來(lái),這個(gè)過(guò)程中有哪些數(shù)據(jù)丟失的風(fēng)險(xiǎn)和可能。
然后我們?cè)僖黄饋?lái)看看,應(yīng)該如何結(jié)合MQ自身提供的一些技術(shù)特性來(lái)保證數(shù)據(jù)不丟失?
二、目前已有的技術(shù)方案
經(jīng)過(guò)之前幾篇文章的討論,目前我們已經(jīng)初步知道,第一個(gè)會(huì)導(dǎo)致數(shù)據(jù)丟失的地方,就是消費(fèi)者獲取到消息之后,沒(méi)有來(lái)得及處理完畢,自己直接宕機(jī)了。
此時(shí)RabbitMQ的自動(dòng)ack機(jī)制會(huì)通知MQ集群這條消息已經(jīng)處理好了,MQ集群就會(huì)刪除這條消息。
那么這條消息不就丟失了么?不會(huì)有任何一個(gè)消費(fèi)者處理到這條消息了。
所以之前我們?cè)敿?xì)討論過(guò),通過(guò)在消費(fèi)者服務(wù)中調(diào)整為手動(dòng)ack機(jī)制,來(lái)確保消息一定是已經(jīng)成功處理完了,才會(huì)發(fā)送ack通知給MQ集群。
否則沒(méi)發(fā)送ack之前消費(fèi)者服務(wù)宕機(jī),此時(shí)MQ集群會(huì)自動(dòng)感知到,然后重發(fā)消息給其他的消費(fèi)者服務(wù)實(shí)例。
手動(dòng)ack機(jī)制之下的架構(gòu)圖如下所示:
當(dāng)時(shí)除了這個(gè)數(shù)據(jù)丟失問(wèn)題之外,還有另外一個(gè)問(wèn)題,就是MQ集群自身如果突然宕機(jī),是不是會(huì)導(dǎo)致數(shù)據(jù)丟失?
默認(rèn)情況下是肯定會(huì)的,因?yàn)閝ueue和message都沒(méi)采用持久化的方式來(lái)投遞,所以MQ集群重啟會(huì)導(dǎo)致部分?jǐn)?shù)據(jù)丟失。
此時(shí)如果消息還沒(méi)來(lái)得及投遞給消費(fèi)者服務(wù),然后MQ集群突然宕機(jī)了,數(shù)據(jù)是不會(huì)丟失的,因?yàn)镸Q集群重啟之后會(huì)自動(dòng)從磁盤文件里加載出來(lái)沒(méi)投遞出去的消息,然后繼續(xù)投遞給消費(fèi)者服務(wù)。
同樣,該方案沉淀下來(lái)的系統(tǒng)架構(gòu)圖,如下所示:
三、數(shù)據(jù)100%不丟失了嗎?
大家想一想,到目前為止,咱們的架構(gòu)一定可以保證數(shù)據(jù)不丟失了嗎?
其實(shí),現(xiàn)在的架構(gòu),還是有一個(gè)數(shù)據(jù)可能會(huì)丟失的問(wèn)題。
那就是上面作為生產(chǎn)者的訂單服務(wù)把消息投遞到MQ集群之后,暫時(shí)還駐留在MQ的內(nèi)存里,還沒(méi)來(lái)得及持久化到磁盤上,同時(shí)也還沒(méi)來(lái)得及投遞到作為消費(fèi)者的倉(cāng)儲(chǔ)服務(wù)。
此時(shí)要是MQ集群自身突然宕機(jī),咋辦呢?
尷尬了吧,駐留在內(nèi)存里的數(shù)據(jù)是一定會(huì)丟失的,我們來(lái)看看下面的圖示。
四、按需制定技術(shù)方案
現(xiàn)在,我們需要考慮的技術(shù)方案是:訂單服務(wù)如何保證消息一定已經(jīng)持久化到磁盤?
實(shí)際上,作為生產(chǎn)者的訂單服務(wù)把消息投遞到MQ集群的過(guò)程是很容易丟數(shù)據(jù)的。
比如說(shuō)網(wǎng)絡(luò)出了點(diǎn)什么故障,數(shù)據(jù)壓根兒沒(méi)傳輸過(guò)去,或者就是上面說(shuō)的消息剛剛被MQ接收但是還駐留在內(nèi)存里,沒(méi)落地到磁盤上,此時(shí)MQ集群宕機(jī)就會(huì)丟數(shù)據(jù)。
所以首先,我們得考慮一下作為生產(chǎn)者的訂單服務(wù)要如何利用RabbitMQ提供的相關(guān)功能來(lái)實(shí)現(xiàn)一個(gè)技術(shù)方案。
這個(gè)技術(shù)方案需要保證:只要訂單服務(wù)發(fā)送出去的消息確認(rèn)成功了,此時(shí)MQ集群就一定已經(jīng)將消息持久化到磁盤了。
我們必須實(shí)現(xiàn)這樣的一個(gè)效果,才能保證投遞到MQ集群的數(shù)據(jù)是不會(huì)丟失的。
五、需要研究的技術(shù)細(xì)節(jié)
這里我們需要研究的技術(shù)細(xì)節(jié)是:倉(cāng)儲(chǔ)服務(wù)手動(dòng)ack保證數(shù)據(jù)不丟失的實(shí)現(xiàn)原理。
之前,筆者就收到很多同學(xué)提問(wèn):
- 倉(cāng)儲(chǔ)服務(wù)那塊到底是如何基于手動(dòng)ack就可以實(shí)現(xiàn)數(shù)據(jù)不丟失的?
- RabbitMQ底層實(shí)現(xiàn)的細(xì)節(jié)和原理到底是什么?
- 為什么倉(cāng)儲(chǔ)服務(wù)沒(méi)發(fā)送ack就宕機(jī)了,RabbitMQ可以自動(dòng)感知到他宕機(jī)了,然后自動(dòng)重發(fā)消息給其他的倉(cāng)儲(chǔ)服務(wù)實(shí)例呢?
這些東西背后的實(shí)現(xiàn)原理和底層細(xì)節(jié),到底是什么?