前言
這篇文章我們討論一下消息中間件如何保證消息不丟失敢会,其實這是分布式系統(tǒng)面臨的一個基本問題曾沈。
分布式系統(tǒng)必然涉及網(wǎng)絡(luò)數(shù)據(jù)傳輸和數(shù)據(jù)存儲,在傳輸方面數(shù)據(jù)會面臨網(wǎng)絡(luò)異常鸥昏,
在存儲方面數(shù)據(jù)會面臨單點故障塞俱、磁盤損壞以及服務(wù)重啟等故障,從而導(dǎo)致數(shù)據(jù)丟失吏垮。
所以障涯,如何確保數(shù)據(jù)不丟失便成了所有分布式系統(tǒng)面臨的一個基本問題罐旗。
接下來,我們以消息中間件為例唯蝶,首先討論一下消息丟失的原因尤莺,然后討論它的解決方案,最后我們在簡單了解一下Kafka具體是如何解決消息丟失的問題生棍。
問題
上圖是最簡單的消息生產(chǎn)與消費模型圖,圖中生產(chǎn)者表示消息的發(fā)送方媳谁,消費者表示消息的接受方涂滴,箭頭表示的是網(wǎng)絡(luò)連接,中間的消息中間件用于傳輸和存儲消息晴音。
我們知道柔纵,當(dāng)消息從生產(chǎn)者發(fā)送到消息中間件的過程中,可能會因網(wǎng)絡(luò)問題導(dǎo)致消息發(fā)送失敗锤躁,如果沒有其它措施搁料,那么消息就會丟失。
即使消息成功到達消息中間件系羞,也會因為存儲方面的問題郭计,導(dǎo)致消息丟失,比如:不對消息持久化處理而是只存在內(nèi)存中椒振,那么服務(wù)重啟便會導(dǎo)致消息丟失昭伸,即使做持久化處理存儲在磁盤,也有可能因磁盤故障導(dǎo)致消息丟失澎迎。
退一步說庐杨,如果傳輸和存儲都正常,那么也會因為消費機制的問題夹供,導(dǎo)致消息丟失灵份,比如:消息中間件向消費者投遞消息但消費者還未確認消息時就將數(shù)據(jù)刪除或更新消息的消費偏移量。
所以哮洽,任意一款消息中間件無論是Kafka填渠、Rabbitmq還是RocketMQ,為了保證消息不丟失袁铐,都必須處理這三方面的問題:網(wǎng)絡(luò)揭蜒、存儲、消費機制剔桨。
方案
針對以上三個方面的問題屉更,各消息中間件解決問題的邏輯都是一樣的,都是采用確認機制解決網(wǎng)絡(luò)傳輸?shù)膯栴}洒缀,采用持久化和副本解決存儲的問題瑰谜,采用提交機制解決消費機制方面的問題欺冀。
下面,我們分別來看一下對應(yīng)問題的解決方案萨脑。
確認機制
從上面的問題分析中隐轩,我們已經(jīng)知道:生產(chǎn)者將消息發(fā)往消息中間件的過程中,可能會因網(wǎng)絡(luò)問題導(dǎo)致消息丟失渤早;
之所以說可能丟失职车,是因為一次完整的消息發(fā)送過程包含發(fā)送和響應(yīng)二個階段,如果消息在發(fā)送階段發(fā)生網(wǎng)絡(luò)異常鹊杖,那么消息會丟失悴灵,但如果消息在響應(yīng)階段出現(xiàn)網(wǎng)絡(luò)異常,那么消息未必丟失骂蓖。
因此积瞒,為了保證消息不丟失,可能的解決方式有兩種:第一登下、出現(xiàn)異常時重新發(fā)送消息不管消息之前是否發(fā)送成功茫孔,第二、出現(xiàn)異常時先查詢消息狀態(tài)然后再根據(jù)情況選擇是否重試被芳。
因為缰贝,消息的數(shù)據(jù)量不是很大,所以一般情況下消息中間件會選擇第一種方式:當(dāng)生產(chǎn)者沒有收到消息中間件(接收者)的確認回復(fù)(ACK)時筐钟,生產(chǎn)者就不斷地進行重試揩瞪,直到收到ACK或者重試超過一定的閥值為止。
ACK是一種消息發(fā)送確認機制——發(fā)送者和接收者約定篓冲,在消息發(fā)送過程中李破,如果接收者收到了某條信息,那么就向發(fā)送者回復(fù)一個ack字段壹将,用該字段用來標(biāo)示消息是否成功被接收者處理或存儲嗤攻。
持久化
通過確認機制我們可以確保消息可以成功到達消息中間件,但如果消息中間件存儲消息的方式是采用內(nèi)存诽俯,或者是異步定時持久化到磁盤妇菱,那么消息也是有可能丟失的。
因為暴区,當(dāng)服務(wù)器重啟時闯团,內(nèi)存中的數(shù)據(jù)便會丟失,即使是異步刷盤也會造成部分數(shù)據(jù)來不及持久化而丟失仙粱,之所以將消息存在內(nèi)存中是為了提高消息發(fā)送和消費的處理效率房交。
因此,為了保證數(shù)據(jù)(消息)不在消息中間件中丟失同時又能保證消息的發(fā)送和消費效率伐割,大多數(shù)消息中間件以及數(shù)據(jù)庫都會采用追加日志的方式順序地將變化的數(shù)據(jù)先寫入到日志文件中候味。
這樣刃唤,通過順序地將數(shù)據(jù)寫入日志文件中,既保證了數(shù)據(jù)持久化也保證了數(shù)據(jù)處理的效率白群。
副本
雖然持久化可以保證發(fā)送成功的消息不丟失尚胞,但是如果出現(xiàn)磁盤損壞或者單點故障的情況,那么相當(dāng)于消息丟失了帜慢。
因此笼裳,在其它服務(wù)器上冗余相應(yīng)的數(shù)據(jù),便成了分布式數(shù)據(jù)庫軟件粱玲,解決單點故障的唯一方案侍咱。
實現(xiàn)方式有兩種一種是客戶端往不同的數(shù)據(jù)服務(wù)器發(fā)送同一份數(shù)據(jù),直到這些服務(wù)器都收到為止密幔;另一個方式是將數(shù)據(jù)發(fā)往集群中的Leader節(jié)點,然后由Leader同步到Follower中撩轰,然后再響應(yīng)客戶端的請求胯甩。
其中第二種方式是主流的數(shù)據(jù)副本的處理方式,其實現(xiàn)原理也很簡單堪嫂,如下圖所示:
當(dāng)消息中間件中的Leader節(jié)點收到Producer發(fā)過來的消息后偎箫,它會將該消息同步給它的Follower節(jié)點,當(dāng)所有的Follower節(jié)點都收到了同步的數(shù)據(jù)后皆串,Leader便響應(yīng)Producer的請求淹办。
當(dāng)然,同步的Follower數(shù)量以及是同步進行還是異步恶复,這些要根據(jù)實際情況來決定怜森。
提交機制
當(dāng)保證了消息在生產(chǎn)者和消息中間件中不會丟失后,最后我們還需要保證在消費者消費消息的時候消息也"不丟失"谤牡。
這里的不丟失副硅,其實是每一條消息都要能被消費者消費不能出現(xiàn)遺漏,這就和具體中間件的消費機制有關(guān)翅萤。
最常見的一種消息消費機制恐疲,是通過Offset來維護消費者消費消息的進度。
比如說套么,消息中間件中有一個消息隊列A培己,里面有從0到100編好序號的100條消息,有一個消費者B胚泌,它要從隊列中獲取消息省咨;
假設(shè),在沒有Offset的情況诸迟,消費者B從0開始每次拉取一條處理一條茸炒,當(dāng)它拉去到50時愕乎,此時消費者B掛了,重啟之后從什么地方開始壁公?
也許你會說記錄一下消息的消費進度感论?對,這個消費進度就是Offset紊册,但它不能由消費者進行維護而是由消息中間件進行維護比肄。
那么接著的問題便是,消息中間件在什么時候跟新Offset呢囊陡?如果在消息中間件將消息投遞給消費者后芳绩,但消費者還未處理之前,消息中間件就跟新offset撞反,
那么在消費者掛了重啟后妥色,就會出現(xiàn)某些消息沒有消費到的情況。顯然遏片,只能將Offset的更新即提交Offset的權(quán)限交給消費者嘹害,這樣消費者消費完數(shù)據(jù)后主動請求更新Offset便不會導(dǎo)致消息消費不到的情況出現(xiàn)。
Kafka
Kafka的配置中有一配置項spring.kafka.producer.acks吮便,它有三個可選值如下所示:
ack=0:相當(dāng)于不需要消息確認笔呀,發(fā)出去之后就不管Broker是否收到,哪怕網(wǎng)絡(luò)異常丟失也不管髓需。
ack=1(默認):只要Leader節(jié)點收到了消息许师,就返回ACK,不管Follower有沒有收到僚匆。
ack=-1(ALL):要求Leader和所有正常的Follower都收到微渠,才放回ACK。
Kafka的Offset提交機制有兩種方式咧擂,一種是自動提交敛助,一種是手動提交。
自動提交是在一定時間間隔后屋确,Kafka客戶端將當(dāng)前消費者的Offset進度纳击,在后端自動提交給Broker,這不僅會存在消息丟失的可能攻臀,而且還有可能導(dǎo)致消息被重復(fù)消費焕数。
因此,對消息丟失敏感的應(yīng)用都會選擇手動提交刨啸,雖然也有可能導(dǎo)致消息重復(fù)消費堡赔,但最少可以保證消息沒有丟失的可能。
Kafka在持久化和副本方面的處理方式设联,大致的邏輯上面已經(jīng)提到過了善已,具體實現(xiàn)細節(jié)比較復(fù)雜灼捂,由于篇幅有限就不詳細細說了。
總結(jié)
為了保證消息不丟失换团,那么生產(chǎn)者在消息發(fā)送的時候需要有確認機制悉稠,消費者在消費消息的時候需要有提交機制,以及消息中間件需要對消息做持久化處理以及采用多副本策略存儲消息艘包,這樣就可以保證絕大多數(shù)的消息是不會丟失的的猛。
之所以說絕大多少,是因為消息在生產(chǎn)者這邊如果采用的是異步的方式而且沒有做持久化存儲想虎,那么消息也是有丟失的可能的卦尊。
擴展閱讀
架構(gòu)設(shè)計思維篇之結(jié)構(gòu)
架構(gòu)設(shè)計事務(wù)篇之Mysql事務(wù)原理