您好,登錄后才能下訂單哦!
本文以消息中間件為例,為大家分析消息中間件的可靠性消息傳遞,閱讀完整文相信大家對(duì)消息中間件的可靠性消息傳遞有了一定的認(rèn)識(shí)。
消息中間件的可靠性消息傳遞,是消息中間件領(lǐng)域非常重要的方案落實(shí)問題(在這之前的MQ理論,MQ選型是抽象層次更高的問題,這里不談)。
并且這個(gè)問題與日常開發(fā)是存在較大的關(guān)聯(lián)的??梢赃@么說,凡是使用了MQ的,機(jī)會(huì)都要考慮這個(gè)問題。當(dāng)然也有一些原始數(shù)據(jù)采集,日志數(shù)據(jù)收集等應(yīng)用場(chǎng)景對(duì)此沒有過高要求。但是大多數(shù)的業(yè)務(wù)場(chǎng)景,對(duì)此還是有著較高要求的。比如訂單系統(tǒng),支付系統(tǒng),消息系統(tǒng)等,你弄丟一條消息,嘿嘿。
網(wǎng)上對(duì)于這方面的博客,大多從單一MQ,或者干脆就是在論述MQ。我不喜歡這樣的論述,這樣的論述太過局限,也過于拖沓。
這次,主要從理論方面論證消息的可靠性傳遞的落實(shí)。具體技術(shù),都是依據(jù)這些理論的,具體實(shí)現(xiàn)都差不多。不過為了便于大家理解,我在文中會(huì)以RabbitMq,Kafka這兩個(gè)主流MQ稍作舉例。
在日常開發(fā)中,我更傾向于在具體開發(fā)前,先整理思路,走通理論,再開始編碼。畢竟,如果連理論都走不同,還談什么編碼。
另外,我按照消息可靠性層次逐步推進(jìn),形成相應(yīng)的目錄,希望大家喜歡(因?yàn)槲艺J(rèn)為,相較網(wǎng)上這方面現(xiàn)有博客的目錄,這樣的目錄更合理,更人性化)。
這里簡(jiǎn)單談一些有關(guān)消息可靠性傳遞的理論。
消息在消息系統(tǒng)(生產(chǎn)者+MQ+消費(fèi)者),其消費(fèi)的次數(shù),無非一下三種情況:
這也代表著消息系統(tǒng)的消息可靠性的三個(gè)層次:
實(shí)現(xiàn)上述三個(gè)層次,需要逐步從三個(gè)方面考慮:
上述三個(gè)層次,對(duì)系統(tǒng)的性能損耗,系統(tǒng)復(fù)雜度等都是逐步上升的。
當(dāng)然,我們首先,需要了解這三個(gè)層次分別如何實(shí)現(xiàn)。
再在實(shí)際開發(fā)中,根據(jù)需要,靈活選取合適方案。
這個(gè)方案是最簡(jiǎn)單的,只要確保消息系統(tǒng)的正確運(yùn)作,以及系統(tǒng)的連通性即可。在正常情況下,可以保證絕大部分?jǐn)?shù)據(jù)的可靠性傳遞。但是仍舊存在極小數(shù)據(jù)的丟失,并且數(shù)據(jù)的丟失會(huì)因?yàn)橄㈥?duì)列的選擇,以及消息并發(fā)量,而受到影響。
可以應(yīng)用于日志上傳這樣對(duì)消息可靠性要求低的應(yīng)用場(chǎng)景。
如果數(shù)據(jù)量不大的情況下,推薦使用RabbitMQ,其消息可靠性在地?cái)?shù)據(jù)量下,是最可靠的。但是在達(dá)到萬級(jí)并發(fā)時(shí),會(huì)存在消息丟失,丟失的比例可以達(dá)到千分之一。
如果數(shù)據(jù)量較大的情況下,要么采用集群。要么就采用Kafk(Kafka可支持十萬級(jí)并發(fā))
一般來說,這種消息可靠性多見于項(xiàng)目初建,或類似日志采集,原始數(shù)據(jù)采集這樣的特定場(chǎng)景。
這個(gè)方案開始利用MQ提供的特定機(jī)制,來提高消息傳遞的可靠性。
該方案的實(shí)現(xiàn)組成,由以下三個(gè)方面構(gòu)成:
通過以上三個(gè)方面的落實(shí),確保可消息一定被下游服務(wù)消費(fèi)。
消息的可靠生產(chǎn),是通過回調(diào)確認(rèn)機(jī)制,確保消息一定被消息服務(wù)器接收。
消息生產(chǎn),發(fā)送給消息服務(wù)器后,消息服務(wù)器會(huì)返回一個(gè)確認(rèn)信息,表示數(shù)據(jù)正常接收。
如果生產(chǎn)者在一定時(shí)間內(nèi)沒有接收到確認(rèn)信息,就會(huì)觸發(fā)重試機(jī)制,進(jìn)行消息的重發(fā)。
如RabbitMq的comfirm機(jī)制,Kafka的acks機(jī)制等。
RabbitMq的confirm機(jī)制存在三個(gè)模式:
這三個(gè)模式,看名稱就可以知道具體作用了。
至于Kafka的acks機(jī)制,同樣存在三個(gè)模式:
說到這里,簡(jiǎn)單說一下,上述的操作可能造成消息的重復(fù)生產(chǎn)。
最簡(jiǎn)單的例子,消息成功發(fā)送,但是對(duì)應(yīng)的消息確認(rèn)信息由于網(wǎng)絡(luò)波動(dòng)而丟失。那么生產(chǎn)者就會(huì)重復(fù)發(fā)送該消息,所以消息服務(wù)器接收到了兩條相同消息,故產(chǎn)生了消息的重復(fù)生產(chǎn)。
另外,上述的重試,都是存在響應(yīng)時(shí)長(zhǎng)判斷(超出1min,就認(rèn)為數(shù)據(jù)丟失),以及重試次數(shù)限制(超過5次,就不進(jìn)行重試。否則,大量重試數(shù)據(jù)可能會(huì)拖垮整個(gè)服務(wù))。
消息的可靠存儲(chǔ),是確保消息在消息服務(wù)器經(jīng)過,或者說堆積時(shí)不會(huì)因?yàn)殄礄C(jī),網(wǎng)絡(luò)等狀況,丟失消息。
網(wǎng)上很多博客在論述消息的可靠性傳遞時(shí),常常把這點(diǎn)遺漏。因?yàn)樗麄兝硭?dāng)然地認(rèn)為消息隊(duì)列已經(jīng)通過集群等實(shí)現(xiàn)了消息隊(duì)列服務(wù)的可用性,故消息的可靠性存儲(chǔ)也就實(shí)現(xiàn)了。
但是這里存在兩個(gè)問題。第一,可靠性不等于可用性。第二,消息的可靠存儲(chǔ),作為消息可靠性傳遞的一部分,是不可缺失的。
可用性:確保服務(wù)的可用。即對(duì)應(yīng)的服務(wù),可以提供服務(wù)。
可靠性:確保服務(wù)的正確。即對(duì)應(yīng)的服務(wù),提供的是正確的服務(wù)。
區(qū)別:我瀏覽淘寶,淘寶頁面打不開,這就涉及了可用性問題(可用性計(jì)算公式:可用時(shí)間/全部時(shí)長(zhǎng)*100%)。而我瀏覽淘寶,查詢訂單,給我顯示的是別人的訂單,這就涉及了可靠性問題。
另外這里再糾正一點(diǎn),可靠性并不依賴于可用性。即使我打不開淘寶頁面,我也不能說淘寶提供訂單查詢就有問題(只是如果沒有了可用性,談?wù)摽煽啃允欠浅]有意義的。畢竟都用不了了,誰還關(guān)心其內(nèi)容是否正確呢,都看不到)
消息隊(duì)列的可用性,是通過多個(gè)節(jié)點(diǎn)構(gòu)成集群,避免單點(diǎn)故障,從而提升可用性。
消息隊(duì)列的可靠存儲(chǔ),是通過備份實(shí)現(xiàn)(這里不糾結(jié)備份如何確保正確)的。如RabbitMq集群的MemNode與DiskNode,又或者Kafka的replication機(jī)制等。
消息的可靠消費(fèi),就是確保消息被消費(fèi)者獲取,并被成功消費(fèi)。避免由于消息丟失,或者消費(fèi)者宕機(jī)而造成消息消費(fèi)不成功,最終造成消息的丟失(因?yàn)镽abbitMq服務(wù)器在認(rèn)為消息被成功消費(fèi)后,將對(duì)應(yīng)數(shù)據(jù)刪除或標(biāo)記為“已消費(fèi)”)。
至于消息的可靠消費(fèi),核心理念還是重試,重試,再重試。不過具體的實(shí)現(xiàn)就八仙過海,各顯神通了。
這里分別說一下RabbitMq,Kafka,Rocket三者對(duì)于可靠消費(fèi)的處理:
提供ack機(jī)制。默認(rèn)是auto,直接在拿到消息時(shí),直接ack。確保了消息到達(dá)了消費(fèi)者,但是無法解決消費(fèi)者消費(fèi)失敗這樣的問題。
實(shí)際開發(fā)中,為了確保消息的可靠消費(fèi),一般會(huì)設(shè)置為munal,只有在程序正確運(yùn)行后,才會(huì)調(diào)用對(duì)應(yīng)api,表示消息正確消費(fèi)。
由于Kafka的消息是落地到硬盤文件的,而且Kafka的消息分發(fā)方式是pull的,所以消息的拉取是通過offset機(jī)制去確認(rèn)對(duì)應(yīng)位置消息的。
當(dāng)然,Kafka的offset默認(rèn)是自動(dòng)提交的(可通過nable_auto_commit與auto_commit_interval_ms控制)。
所以消費(fèi)者調(diào)用服務(wù)失敗等原因,可以通過手動(dòng)offset提交,來實(shí)現(xiàn)對(duì)數(shù)據(jù)的重復(fù)消費(fèi)(甚至是歷史數(shù)據(jù)的消費(fèi)),也就可以在消費(fèi)失敗時(shí)對(duì)同一消息進(jìn)行再消費(fèi)。
如果是消費(fèi)者宕機(jī)等原因,由于Kafka服務(wù)器沒有收到對(duì)應(yīng)的offset提交,所以認(rèn)為那條消息沒有被消費(fèi)成功,故返回的依舊是那條消息。
其實(shí)RocketMq的處理有些類似Kafka確認(rèn)機(jī)制+RabbitMq死信隊(duì)列的感覺。
首先,消費(fèi)者從RocketMq拉取消息,如果成功消費(fèi),就返回確認(rèn)消息。
如果未成功消費(fèi),就嘗試重新消費(fèi)。
嘗試消費(fèi)一定次數(shù)后(如5次),就會(huì)將該消息發(fā)送之RocketMq中的重試隊(duì)列。
如果遇到消費(fèi)者宕機(jī)的情況,RocketMq會(huì)認(rèn)為該消息未成功消費(fèi),會(huì)被其他消費(fèi)者繼續(xù)消費(fèi)。
其實(shí)在RabbitMq的可靠性消費(fèi)時(shí),我們也會(huì)將多次消費(fèi)失敗的數(shù)據(jù)保存下來,便于后期修復(fù)等。不過保存的方式由很多種,日志,數(shù)據(jù)庫,消息隊(duì)列等。而RocketMq則給出了具體的落實(shí)方案。
上述的操作,可能造成消息的重復(fù)消費(fèi)。
最簡(jiǎn)單的例子,消息成功被消費(fèi)者消費(fèi),但是消費(fèi)者還沒來得及發(fā)送確認(rèn)信息,就宕機(jī)了。
消息隊(duì)列由于沒有收到確認(rèn)消息,認(rèn)為該條消息尚未被消息,就將該消息交由其他消費(fèi)者繼續(xù)消費(fèi)。
這個(gè)方案,就是通過MQ以外的應(yīng)用程序,來進(jìn)行擴(kuò)展,最終達(dá)到消息準(zhǔn)確消費(fèi)的目的。
那么為什么不將這個(gè)功能,囊括在MQ中呢?
個(gè)人認(rèn)為有四個(gè)方面的考慮:
消息存儲(chǔ)部分的準(zhǔn)確存儲(chǔ),不該我們來操心,所以只闡述消息生產(chǎn)與消息消費(fèi)兩個(gè)部分。
綜上來看,就是消息發(fā)出后,到生產(chǎn)者消息確認(rèn)信息的處理之間,出現(xiàn)各種意外,導(dǎo)致重復(fù)生產(chǎn)。
綜上來看,就是消息已經(jīng)被消費(fèi)后,到消息隊(duì)列服務(wù)器進(jìn)行確認(rèn)消息處理之間,出現(xiàn)各種意外,導(dǎo)致重復(fù)消費(fèi)。
解決方案:messageId+冪等
準(zhǔn)確來說,解決方案的核心是冪等,而messageId是作為輔助手段的。
冪等,簡(jiǎn)單說明一下,就是多次操作與單次操作對(duì)系統(tǒng)狀態(tài)的影響是一致的。
如
i = 1;
就是冪等操作,因?yàn)闊o論進(jìn)行幾次,i的值都沒有變化。
而
i++;
則不是冪等操作,因?yàn)閕的值與執(zhí)行次數(shù)息息相關(guān)。
故通過冪等操作來確保同一條消息,不被執(zhí)行多次。
但是,消費(fèi)者如何確定是否為同一條消息呢?
有的消息體存在唯一性字段,如orderId等。但有的消息并沒有這樣的唯一性字段。
所以需要一個(gè)專門的字段,來表示唯一性,并且與業(yè)務(wù)消息解耦。這就是messageId。
既可以采用消息體的唯一性字段(可以是單一字段,也可以是組合字段),也可以通過特定方式生成對(duì)應(yīng)標(biāo)識(shí)。
具體的生成情況,就不在這里贅述了。
先來一張大圖(這種事情,圖片展示最直觀了),展示一下流程:
(圖片是絕對(duì)清晰的。看不清圖片的朋友,請(qǐng)將圖片在新頁面打開,或下載。說實(shí)話,來到新公司,首先提升的就是畫圖能力。囧)
簡(jiǎn)單說一下流程,大家可以對(duì)照著上圖,看一下:
上述中提到的補(bǔ)償機(jī)制,其實(shí)是類似事務(wù)中的一個(gè)操作。通過一個(gè)定時(shí)任務(wù),定時(shí)巡檢數(shù)據(jù)庫處于sending狀態(tài)的message,并通過生產(chǎn)者極性發(fā)送(所以message一般都保存source,target等信息)。
之所以會(huì)有sending狀態(tài)的message,就是因?yàn)榇嬖谏a(chǎn)者消息發(fā)送出去了,還沒收到生產(chǎn)確認(rèn)信息,結(jié)果生產(chǎn)者實(shí)例自己宕機(jī)的情況。
至于補(bǔ)償機(jī)制的定時(shí)任務(wù),是一個(gè)非常簡(jiǎn)單的實(shí)現(xiàn),這里就不再贅述了。
這里進(jìn)行的操作是針對(duì)非冪等的操作。
如果是冪等操作,則可以直接進(jìn)行。畢竟多次執(zhí)行與單次執(zhí)行對(duì)數(shù)據(jù)庫的影響是一致的。
但是注意冪等操作在部分場(chǎng)景下無效的問題(時(shí)間影響上),如“余額 = 1k”的操作對(duì)于數(shù)據(jù)庫而言是冪等的,但是在兩次“余額 = 1k”操作間,有一個(gè)“余額 = 2k”的操作,則會(huì)發(fā)生問題(丟失了“余額 = 2k”操作)。當(dāng)然,這種類似ABA問題,完全可以引入版本號(hào),來進(jìn)行解決。
綜上,還是推薦采用以下解決方法,流程較為簡(jiǎn)單:
至此,消息的準(zhǔn)確傳遞就完成了。
看完上述內(nèi)容,你們對(duì)消息中間件的可靠性消息傳遞有進(jìn)一步的了解嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。