楊成榮 劉峻松 孫新杰 吳超
摘要:隨著微服務(wù)架構(gòu)的興起,越來越多的應(yīng)用由單體式向微服務(wù)架構(gòu)轉(zhuǎn)移,由于微服務(wù)其分布式的特性,以及微服務(wù)架構(gòu)自身強(qiáng)調(diào)數(shù)據(jù)的獨立,整體的數(shù)據(jù)一致性問題被放大。該文主要針對在微服務(wù)通訊過程中引入分布式消息隊列進(jìn)行研究。首先,解決消息和指令的冪等性,保證重復(fù)消息的單次執(zhí)行。其次,在分布式消息隊列中對消息數(shù)據(jù)進(jìn)行落盤保存通過偏移量的操作實現(xiàn)單方重新上線后的自我補(bǔ)償,保證消息的投遞。最后,人為模擬業(yè)務(wù)流程和可能出現(xiàn)的問題進(jìn)行試驗,由該機(jī)制進(jìn)行自動處理,實現(xiàn)數(shù)據(jù)的最終一致性。通過分析實驗結(jié)果,該機(jī)制可以在低時效性業(yè)務(wù)系統(tǒng)中自動解決由于服務(wù)或數(shù)據(jù)庫離線造成的數(shù)據(jù)不一致,實現(xiàn)微服務(wù)整體的數(shù)據(jù)的最終一致性。
關(guān)鍵詞:微服務(wù);分布式;數(shù)據(jù)一致性
中圖分類號:TP311 文獻(xiàn)標(biāo)識碼:A
文章編號:1009-3044(2020)08-0266-03
隨著互聯(lián)網(wǎng)業(yè)務(wù)的復(fù)雜性和其用戶量的不斷增加,單體式應(yīng)用的弊端已經(jīng)逐步顯現(xiàn),微服務(wù)架構(gòu)應(yīng)運(yùn)而生。微服務(wù)架構(gòu)將巨大臃腫的單體式應(yīng)用根據(jù)業(yè)務(wù)和功能的需求分解為無數(shù)小的且通過不同互聯(lián)技術(shù)相互連通的微服務(wù)[1],其優(yōu)勢主要在于方便部署,擴(kuò)展性高,開發(fā)周期和成本大大降低等[2]。但是,微服務(wù)架構(gòu)自身的分布式特性也存在一定的弊端。在實際生產(chǎn)環(huán)境中,越來越多的行業(yè)領(lǐng)域都開始使用軟件自動化來代替人工,相應(yīng)的,計算機(jī)軟件的穩(wěn)定性、健壯性的影響也越來越大,計算機(jī)軟件出現(xiàn)的錯誤所付出的代價也越來越大,目前微服務(wù)架構(gòu)的興起,也要面臨其所帶來的風(fēng)險,數(shù)據(jù)一致性[3]的保證問題就是其中之一。
目前針對微服務(wù)架構(gòu)在分布式系統(tǒng)方面數(shù)據(jù)不一致情況有了一些研究和解決方案。文獻(xiàn)[4]使用對關(guān)鍵業(yè)務(wù)進(jìn)行整合的方法,將某些關(guān)聯(lián)性極強(qiáng)的服務(wù)整合成一個服務(wù),整合后自身使用事務(wù)管理來保證數(shù)據(jù)一致性。文獻(xiàn)[5]采用了一種危機(jī)應(yīng)對的機(jī)制,當(dāng)任何一處發(fā)生異常時,觸發(fā)一套針對異常的回滾事件,借此來使得整個系統(tǒng)來恢復(fù)到執(zhí)行前的狀態(tài)。文獻(xiàn)[6]針對網(wǎng)絡(luò)連接異常的問題進(jìn)行了討論和研究,設(shè)計了一個鏈路的檢測機(jī)制來做到預(yù)先發(fā)現(xiàn)網(wǎng)絡(luò)問題并進(jìn)行處理,但是對于已經(jīng)發(fā)生的數(shù)據(jù)不一致情況沒有給出解決方案。
1 業(yè)務(wù)流程概述
本文針對微服務(wù)架構(gòu)中存在的問題[7],重新設(shè)計了業(yè)務(wù)流程:業(yè)務(wù)從發(fā)送端的消息列表產(chǎn)生,也從發(fā)送端的消息列表結(jié)束,一個新的指令被創(chuàng)建的時刻將在消息列表進(jìn)行記錄和登記,接下來開始對該指令進(jìn)行執(zhí)行過程,每個關(guān)鍵點的執(zhí)行都要對該列表內(nèi)該事務(wù)狀態(tài)進(jìn)行更新,然后向指定位置發(fā)送消息,消息的接收者從指定話題中消費消息,將消息首先記錄在本地消息列表中,然后再開始后續(xù)的執(zhí)行過程,每個關(guān)鍵點的完成都要對列表內(nèi)狀態(tài)進(jìn)行更新。
2 關(guān)鍵模塊概述
針對以上業(yè)務(wù)流程分別設(shè)計五個關(guān)鍵模塊,分別為業(yè)務(wù)發(fā)起模塊、任務(wù)管理模塊、消息發(fā)送模板、消息接收模塊以及遠(yuǎn)程分布式消息隊列模塊。
2.1 業(yè)務(wù)發(fā)起模塊
業(yè)務(wù)發(fā)起模塊用于對一個新的業(yè)務(wù)進(jìn)行發(fā)起。主要包括兩方面功能,一方面是當(dāng)該模塊處于一個主動發(fā)起業(yè)務(wù)的服務(wù)中時,由服務(wù)在應(yīng)用內(nèi)部向該模塊發(fā)送業(yè)務(wù)相關(guān)內(nèi)容,該模塊直接對該業(yè)務(wù)賦予一個唯一標(biāo)記的Key值,生成相關(guān)任務(wù)條目信息將信息傳送給任務(wù)管理模塊,則為成功發(fā)起一項新的業(yè)務(wù);另一方面當(dāng)該模塊處于一個被動接受指令的服務(wù)時,該模塊從消息接收模塊處接收任務(wù)指令,首先向任務(wù)管理模塊發(fā)送請求對接收到的指令Key值進(jìn)行核對,若該任務(wù)已經(jīng)存在且尚未完成,則直接忽略處理,由任務(wù)管理模塊自行處理,若該任務(wù)以完成,則由任務(wù)管理模塊重新核對完成信息,由消息發(fā)送模塊直接發(fā)送該任務(wù)完成的信息;若該任務(wù)不存在,則同新發(fā)起業(yè)務(wù)流程,生成必要信息遞交任務(wù)管理模塊。
2.2 任務(wù)管理模塊
任務(wù)管理模塊用于對所有已登記記錄的任務(wù)進(jìn)行管理。主要包括以下功能:1)對業(yè)務(wù)發(fā)起模塊發(fā)出的查詢請求進(jìn)行回應(yīng),包括對Key值進(jìn)行查重,向其返回查詢狀態(tài)和該任務(wù)狀態(tài)。2)對業(yè)務(wù)發(fā)起模塊所發(fā)起的業(yè)務(wù)進(jìn)行登記,記錄在任務(wù)列表當(dāng)中,當(dāng)該模塊所在服務(wù)對該條任務(wù)的業(yè)務(wù)流程完成后,更新任務(wù)列表中的任務(wù)完成狀態(tài)為已完成狀態(tài),對由于服務(wù)自身原因無法完成的任務(wù)標(biāo)記為異常狀態(tài)。3)定時掃描所有處于尚未完成狀態(tài)的任務(wù)條目,向消息的發(fā)送模塊遞交發(fā)送任務(wù)結(jié)果請求;對于處于尚未完成狀態(tài)但是最后發(fā)送時間較當(dāng)前系統(tǒng)時間差值超過設(shè)定超時時間時,視該任務(wù)為超時,則直接向發(fā)送模塊遞交重發(fā)請求;對超時的任務(wù)但是發(fā)送次數(shù)已經(jīng)超過預(yù)設(shè)閾值的條目進(jìn)行標(biāo)記失敗,記錄該條任務(wù)的全部信息,根據(jù)業(yè)務(wù)需求通知人工干預(yù)處理;對所有處于尚異常狀態(tài)的任務(wù)遞交消息發(fā)送模塊進(jìn)行轉(zhuǎn)發(fā)處理。4)當(dāng)該模塊處于一個任務(wù)的發(fā)起服務(wù)時,將接收從消息接收模塊遞交的完成申請對該條任務(wù)的完成狀態(tài)進(jìn)行更新。
2.3 消息發(fā)送模塊
消息發(fā)送模塊用于根據(jù)其他模塊遞交的發(fā)送申請根據(jù)類型分類并整理為指定消息格式然后進(jìn)行消息的發(fā)送和廣播。該模塊可以通過預(yù)設(shè)或者運(yùn)行過程中動態(tài)改變發(fā)送Topic值來改變發(fā)送的目標(biāo);同時該模塊下設(shè)一個后備列表,當(dāng)該模塊所在的服務(wù)由于種種原因不能完成該項業(yè)務(wù),則想該后備列表查詢與該服務(wù)功能相同的后備服務(wù),然后將該異常任務(wù)直接遞交給后備服務(wù)執(zhí)行。
2.4 消息接收模塊
消息接收模塊包括被動消息監(jiān)聽守護(hù)模塊和主動消息接收模塊兩個子模塊。被動消息監(jiān)聽守護(hù)模塊隨服務(wù)啟動監(jiān)聽固定的Topic,將獲取的消息解讀并根據(jù)分類遞交給業(yè)務(wù)發(fā)起模塊或者任務(wù)管理模塊。主動消息獲取模塊可以在每次服務(wù)啟動時或者是在預(yù)設(shè)閾值時間內(nèi)沒有接收到任何指令的情況下,或者在任務(wù)管理模塊的指令下,使用底層API對遠(yuǎn)程分布式消息對列中當(dāng)前終端的偏移量和本地記錄的最近一次偏移量進(jìn)行比較,若不相同則判定為有消息丟失,則根據(jù)實際情況重復(fù)消費指定偏移量的消息進(jìn)行解讀和遞交。
2.5 遠(yuǎn)程分布式消息隊列模塊
遠(yuǎn)程分布式消息隊列模塊處于發(fā)送與接收之間的中間件。本文使用Apache Kafka分布式隊列[8]作為中間件,在本文所設(shè)計的整體機(jī)制中為所有的消息傳遞進(jìn)行記錄和落盤處理,由于其自身的特性,使用文件與偏移量的形式來實現(xiàn)消息隊列,而且保證了每個用戶分組只能有且只有一個用戶消費到同一條消息,被消費過的消息不可重復(fù)消費,但是在本文設(shè)計的機(jī)制中由于需要對部分異常情況進(jìn)行消息的補(bǔ)償操作,所以使用該平臺提供的低層API進(jìn)行偏移量的操作[9];另外該分布式平臺的落盤處理根據(jù)其節(jié)點的數(shù)量均有不同數(shù)量的備份,可以確保在部分節(jié)點宕機(jī)崩潰離線的情況下整個消息平臺依舊可用且消息不丟失。最紅該平臺可以通過參數(shù)的設(shè)置來根據(jù)實際生產(chǎn)環(huán)境來設(shè)定定時對落盤的過久消息進(jìn)行處理和清理,保證整個系統(tǒng)的資源占用。
3 數(shù)據(jù)一致性保障機(jī)制
根據(jù)上述描述,每個業(yè)務(wù)服務(wù)都可簡單地理解為一個發(fā)送方和一個接收方的數(shù)據(jù)交互的組合,因此根據(jù)這一模型,可能會出現(xiàn)數(shù)據(jù)不一致的情況可能有如下幾種:
1)發(fā)送方成功發(fā)送業(yè)務(wù)請求,接收方成功接收,但此時發(fā)送方服務(wù)離線,在發(fā)送方重啟后,可能會再次發(fā)送業(yè)務(wù)請求造成接收端重復(fù)執(zhí)行相同指令。
2)發(fā)送方成功發(fā)送業(yè)務(wù)請求,此時接收方離線,造成該消息丟失,進(jìn)而該業(yè)務(wù)停滯無法繼續(xù)進(jìn)行。
3)發(fā)送方成功發(fā)送業(yè)務(wù)請求,接收方成功接收該請求,兩方自身業(yè)務(wù)成功執(zhí)行,接收方發(fā)送指令回執(zhí),此時發(fā)送方離線,該消息丟失,在成該業(yè)務(wù)數(shù)據(jù)不一致。
4)發(fā)送方成功發(fā)送業(yè)務(wù)請求,接收方剛剛接收該請求,接收方離線,造成該消息丟失。
除此之外還有很多不可預(yù)料問題造成消息的丟失和消息的重復(fù)接收,進(jìn)而造成數(shù)據(jù)不一致的發(fā)生。
針對以上所可能發(fā)生的問題,根據(jù)微服務(wù)模塊之間多種傳輸模式[10],采取如下一系列策略來保證數(shù)據(jù)的一致性:通過實現(xiàn)消息的冪等性來解決一切重復(fù)消費的問題,具體的實現(xiàn)方法體現(xiàn)在為每一條業(yè)務(wù)流程分配唯一的Key值,在接收到指令后首先對Key值進(jìn)行核對如果出現(xiàn)重復(fù)則忽略該消息。通過引入一個外部的分布式消息隊列來解決一切消息或者信息丟失的問題,具體實現(xiàn)方法體現(xiàn)在所有消息的傳輸均經(jīng)過該消息隊列,該消息隊列對所有的消息進(jìn)行羅盤存儲,然后接收者通過消息隊列進(jìn)行消息的消費,通過一系列設(shè)置保證消息的百分百投遞。通過對每一項業(yè)務(wù)進(jìn)行記錄,保證了所有自身環(huán)境的數(shù)據(jù)不一致的發(fā)生。通過對遠(yuǎn)程消息隊列的偏移量的高級操作來實現(xiàn)在特定情況下對消息進(jìn)行重復(fù)消費來保證數(shù)據(jù)一致性。
4 模擬實驗結(jié)果分析
基于上述機(jī)制,本文使用Java線程模擬微服務(wù)場景,各個模擬微服務(wù)之間通過外部Kafka消息隊列平臺進(jìn)行消息傳輸,然后通過手動阻塞的方式對微服務(wù)交互的每個階段可能出現(xiàn)的離線情況進(jìn)行模擬,用來檢測該機(jī)制的處理情況。
首先模擬正常通訊過程,服務(wù)A發(fā)起一個新的任務(wù)向服務(wù)B發(fā)送任務(wù)請求,同時執(zhí)行自身業(yè)務(wù)邏輯,服務(wù)B接收到任務(wù)后執(zhí)行自身業(yè)務(wù)邏輯,完成后向服務(wù)A發(fā)送完成回執(zhí),整個業(yè)務(wù)流程結(jié)束。
第一次模擬服務(wù)A在發(fā)送指令之后崩潰離線,重啟后重新執(zhí)行該指令,造成對服務(wù)B的同一條指令的兩次重復(fù)傳遞,服務(wù)B根據(jù)本條業(yè)務(wù)的Key值確定為重復(fù)指令,重復(fù)向服務(wù)A發(fā)送任務(wù)回執(zhí),保證了消息的冪等性。
第二次模擬服務(wù)A發(fā)起業(yè)務(wù)并向服務(wù)B發(fā)送任務(wù)請求,服務(wù)B成功接收請求并執(zhí)行自身業(yè)務(wù)邏輯,完成后向服務(wù)A發(fā)送任務(wù)完成回執(zhí),服務(wù)A成功接收回執(zhí),但是在對自身任務(wù)列表狀態(tài)更改之前,服務(wù)A異常崩潰離線,重啟后,根據(jù)啟動機(jī)制,首先向遠(yuǎn)端消息列表查詢當(dāng)前偏移量和本地最后一次執(zhí)行的偏移量做對比,發(fā)現(xiàn)出現(xiàn)誤差,則前移偏移量重復(fù)消費上次的內(nèi)容,獲得服務(wù)B發(fā)送的消息回執(zhí),則此次業(yè)務(wù)流程完成。
第三次模擬服務(wù)A發(fā)起業(yè)務(wù)并向服務(wù)B發(fā)送任務(wù)請求,服務(wù)B成功接收請求,但是在將請求記錄在自身任務(wù)列表之前服務(wù)B異常崩潰,重啟后,根據(jù)服務(wù)B自身啟動機(jī)制,核對遠(yuǎn)端當(dāng)前偏移量與自身本地偏移量,發(fā)現(xiàn)異常并重復(fù)消費,獲取服務(wù)A發(fā)送的任務(wù)請求,正常執(zhí)行業(yè)務(wù)流程。
第四次模擬服務(wù)B接收到服務(wù)A的服務(wù)指令,但是在執(zhí)行自身業(yè)務(wù)邏輯時由于自身問題無法完成該業(yè)務(wù),則發(fā)送求助,此時服務(wù)C和服務(wù)D同處于該求助列表中,有且只有一個服務(wù)消費到了這條求助并執(zhí)行B的任務(wù),向A發(fā)送回執(zhí),保證該次業(yè)務(wù)流程完成。
第五次模擬服務(wù)A發(fā)起一次業(yè)務(wù)流程,向服務(wù)B發(fā)送任務(wù)請求,但是由于一部分原因,服務(wù)B自身無法接收到該指令,因此,服務(wù)A在超時后再次向服務(wù)B發(fā)送同一條指令,發(fā)送超過規(guī)定次數(shù)后,服務(wù)A認(rèn)為服務(wù)B已經(jīng)離線,并且無法完成任務(wù),于是向后備列表發(fā)送求助,此時服務(wù)C和服務(wù)D同時接收到求助,有且只有一個服務(wù)消費此求助,然后完成任務(wù)向服務(wù)A發(fā)送回執(zhí)。
本文設(shè)計并實現(xiàn)了在微服務(wù)架構(gòu)中針對服務(wù)間數(shù)據(jù)交互中可能出現(xiàn)的各種導(dǎo)致數(shù)據(jù)不一致的問題的解決方案,該機(jī)制通過保證消息的冪等性,消息的落盤和每個服務(wù)對于偏移量的操作基本實現(xiàn)在較低的時延內(nèi)在低時效性的業(yè)務(wù)模型中保證數(shù)據(jù)的最終一致性,通過人為模擬業(yè)務(wù)各個步驟當(dāng)中可能出現(xiàn)的各類角色的離線情況驗證該機(jī)制可以預(yù)防一定的數(shù)據(jù)不一致并且在數(shù)據(jù)不一致發(fā)生時能夠觸發(fā)一定的事件來對整個業(yè)務(wù)邏輯進(jìn)行補(bǔ)償,而且能夠盡可能的單方自行解決,避免了在耗時較高的業(yè)務(wù)中重發(fā)等待時間過長導(dǎo)致的業(yè)務(wù)時延成倍增加問題,并且將該機(jī)制設(shè)計為服務(wù)模塊可以隨服務(wù)啟動又可以獨立于業(yè)務(wù)邏輯之外來保證該機(jī)制的運(yùn)行。
參考文獻(xiàn):
[1] Dragoni N.Giallorenzo S.Lafuente A L et al-Microservices:yesterday, today, and tomorrow[Ml//Present and Ulterior Soft-ware Engineering. Cham: Springer Intemational Publishing,2017: 195-216.
[2] Pahl C,Jamshidi P.Microservices:a systematic mapping study[C]//Proceedings of the 6th International Conference on Cloud Computing and Services Science, April 23-25, 2016. Rome,It一aly. SCITEPRESS - Science and and Technology Publications, 2016: 137-146.
[3]周婧,王意潔,阮煒,等.面向海量數(shù)據(jù)的數(shù)據(jù)一致性研究[Jl-計算機(jī)科學(xué),2006,33(4):137-140,161.
[4]徐進(jìn),黃勃,馮炯.基于消息通信的分布式系統(tǒng)最終一致性平臺[J].計算機(jī)應(yīng)用,2017,37(4):1157-1163.
[5]孫赫勇.基于企業(yè)服務(wù)總線消息補(bǔ)償方法的設(shè)計[J].微型機(jī)與應(yīng)用,2013,32(10):90-91.
[6]姜夢蘭.基于消息中間件服務(wù)可靠性保障方案的研究與實現(xiàn)[D].成都:電子科技大學(xué),2010.
[7]李貞吳.微服務(wù)架構(gòu)的發(fā)展與影響分析[Jl.信息系統(tǒng)工程, 2017(1):154-155.
[8] Garg N.Apache Kafka[M]. 2013.
[9]杜岳峰,申德榮,聶鐵錚,等.基于關(guān)聯(lián)數(shù)據(jù)的一致性和時效性清洗方法[J].計算機(jī)學(xué)報,2017,40(1):92-106.
[10] Thones J.Microservices[J]. IEEE Software, 2015, 32(1):116.
【通聯(lián)編輯:謝媛媛】
收稿日期:2020-01-25
基金項目:教育大數(shù)據(jù)視域下學(xué)習(xí)干預(yù)模型構(gòu)建與研究(項目編號:LPSSY201908)
作者簡介:楊成榮(1993-),男,河北衡水人,助教,碩士,研究方向為機(jī)器學(xué)習(xí)、數(shù)據(jù)處理;劉峻松(1995一),男,山東青島人,碩士,研究方向為數(shù)據(jù)處理;孫新杰(1987-),男,河南駐馬店人,副教授,碩士,研究方向為數(shù)據(jù)挖掘;吳超(1993-),男,江西鷹潭人,講師,碩士,研究方向為數(shù)據(jù)處理。