作者:京東零售 徐開廷
本文大約1.7萬字,閱讀需要13分鐘。
導(dǎo)讀:近幾年,除AIGC外,軟件領(lǐng)域相關(guān)比較大的變化,就是各相關(guān)業(yè)務(wù)領(lǐng)域開始如火如荼地建設(shè)中臺和去中臺化了。本文不探討中臺對公司組織架構(gòu)涉及的變化和影響,只是從中臺化演進(jìn)的思路,及使用的底層支撐技術(shù)框架進(jìn)行分析探討,重點(diǎn)對中臺及前臺協(xié)作涉及到的擴(kuò)展點(diǎn)及熱部署包的底層技術(shù)細(xì)節(jié),結(jié)合京東實際落地情況,對涉及的核心技術(shù)框架進(jìn)行源碼初探分析,探討技術(shù)框架的考慮點(diǎn),拓寬大家的思路,歡迎大家審閱。
1、序言
中臺及其建設(shè),區(qū)別于單體應(yīng)用建設(shè)及微服務(wù)建設(shè),最大的差異在于中臺建設(shè)有一個較為獨(dú)特的概念,即前臺角色。中臺建設(shè)并采用標(biāo)準(zhǔn)協(xié)議,開放了一系列標(biāo)準(zhǔn)豐富的能力供前臺角色去編排、擴(kuò)展使用。從外部的視角來看,其實看不到中臺和前臺,單純還是功能交付,外界的感知沒有太大的差異;從產(chǎn)研的視角來看,功能交付是中臺能力疊加一系列前臺個性化能力的結(jié)合物,職責(zé)上期望通過中臺底層能力建設(shè)和前臺個性化能力增強(qiáng),兩方獨(dú)立開發(fā),再通過底層技術(shù)支撐框架來將兩方能力進(jìn)行結(jié)合,期望達(dá)到中臺、前臺角色分工清晰,獨(dú)立交付,提升交付速度的美好愿望。
從上述定義就可以看到,在外部視角感知不強(qiáng)的情況下,交付速度其實是個很明顯的衡量指標(biāo)。中臺建設(shè)成功的標(biāo)準(zhǔn),重點(diǎn)并不在于對中臺建設(shè)的豐富能力進(jìn)行衡量,也不在于前臺開發(fā)了多少獨(dú)立的擴(kuò)展能力,仍在于同之前未建設(shè)中臺相比,交付速度是否有質(zhì)的提升。若這個關(guān)鍵指標(biāo)沒有變化,預(yù)估此類建設(shè)思路也會出現(xiàn)相關(guān)的變化及轉(zhuǎn)型,轉(zhuǎn)型的下一步思路和方法也有不少,不在這里探討。
后續(xù)的內(nèi)容,均會聚焦中臺的底層技術(shù)支撐框架(后文簡稱為Matrix),來將中臺、前臺兩方能力進(jìn)行結(jié)合的技術(shù)細(xì)節(jié)和考慮點(diǎn)進(jìn)行展開。
在筆者看來,Matrix框架,作為京東實施PaaS化相關(guān)的技術(shù)解決方案,期望的是建立劃分合理的業(yè)務(wù)領(lǐng)域,完成業(yè)務(wù)建模及抽象,分離出核心邏輯及高頻個性化業(yè)務(wù),并將個性化的邏輯隔離出來,期望交給名為前臺的組織或者部門去共建開發(fā),來達(dá)成中臺邏輯穩(wěn)定,前臺可以并行開發(fā),最終提升交付效率的目的。
當(dāng)然,在京東或者其他商業(yè)化公司,應(yīng)用系統(tǒng)覆蓋的業(yè)務(wù)領(lǐng)域及技術(shù)方向存在多種,包括但不限于各端的APP等前臺、各端的web前臺、各應(yīng)用后端系統(tǒng)、各數(shù)據(jù)算法平臺、各報表運(yùn)營平臺等等。目前的Matrix框架對其他某些領(lǐng)域可能存在不適應(yīng)的情況,在筆者看來,著實屬于正常情況。世界上的工具萬萬千,刀槍劍戟 斧鉞鉤叉 閑棍槊棒 鞭锏錘抓;各類解決方案千千萬,很難存在一個錘子可以砸所有的釘子。但是只要總體的理念是合適的,并為其他各類適配此理念的工具提供合適成長的土壤,預(yù)判此解決方案可以逐步豐滿成熟。
2、底層支撐框架分析
分析前提:我們只聚焦與中臺底層支撐框架,其他如中臺的業(yè)務(wù)領(lǐng)域切分、領(lǐng)域建模等內(nèi)容暫不涉及。需要注意的是,本次原理探究,是從使用人員及業(yè)務(wù)應(yīng)用的角度出發(fā),而非從真正對Matrix設(shè)計者的角度出發(fā),對Matrix原理的探索難免會存在以偏概全的毛病,對其全貌也必然會存在描述不準(zhǔn)確的情況。加之框架底層也在不斷豐富和完善,很多技術(shù)內(nèi)容存在不斷變更和發(fā)展,本文描述的內(nèi)容在短時間內(nèi)也會存在描述不再準(zhǔn)確的情況。但是好在基本原理應(yīng)該是準(zhǔn)確的,內(nèi)容描述的過程中,如有錯誤,歡迎大家指正。
本文重點(diǎn)對底層支撐框架涉及的幾個核心技術(shù)點(diǎn)進(jìn)行展開:前臺包熱部署設(shè)計原理、前中臺隔離原理、前臺業(yè)務(wù)身份設(shè)計原理。
3、前臺包熱部署設(shè)計原理
在中臺化的實施路徑上,伴隨的節(jié)奏一般為:中臺能力改造升級-->中臺開放標(biāo)準(zhǔn)擴(kuò)展能力-->前臺使用標(biāo)準(zhǔn)擴(kuò)展能力完成個性化開發(fā)-->前中臺發(fā)布集成。其中發(fā)布集成有2種可選的方式,一種是中臺和前臺采用手工半自動化的方式進(jìn)行集成,另外一種是中臺和前臺使用全自動的方式進(jìn)行集成。全自動的方式,就免不了需要將前臺的功能包,采用“熱部署”的方式部署至中臺相關(guān)的應(yīng)用系統(tǒng)中。
本文對前臺包發(fā)布、熱部署的技術(shù)方案及思路進(jìn)行探究,讓我們的讀者了解其設(shè)計理念。這樣以后在工作中遇到類似前臺包發(fā)布相關(guān)的問題,亦或者后續(xù)有機(jī)會使用并研究其他類似的技術(shù)框架,均可以做到舉一反三,心中了然。
3.1、熱部署基本原理
在開始介紹之前,假定我們作為底層框架的設(shè)計方,可以嘗試回答下方的三個問題,并與后面給出的結(jié)果進(jìn)行對比,看看與自己預(yù)判的答案是否一致。若不一致,本文應(yīng)該對你有些幫助,可以細(xì)細(xì)品鑒;若一致,那么恭喜你,說明你的技術(shù)功底相當(dāng)扎實,對相關(guān)的原理及實踐了解透徹。
問題:
1)使用前臺擴(kuò)展包,在發(fā)布平臺操作完成(完成推送、生效)后,應(yīng)用端進(jìn)行擴(kuò)容上線,擴(kuò)展點(diǎn)包是否可以自動拉取加載、自動掛載運(yùn)行?
2)使用前臺擴(kuò)展包,在發(fā)布平臺操作完成(完成推送、生效)前,應(yīng)用端進(jìn)行擴(kuò)容上線,擴(kuò)展點(diǎn)包是否可以自動拉取加載、自動掛載運(yùn)行?
3)使用前臺擴(kuò)展包,在發(fā)布平臺對部分容器完成前臺包灰度推送,但沒有觸發(fā)生效,此時對這些容器執(zhí)行重啟操作,推送的前臺包是否會掛載運(yùn)行?
4)在測試環(huán)境亦或是其他環(huán)境,一不小心刪除了熱部署包路徑及對應(yīng)的文件,會有什么問題?如何解決?
在開始回答上面的問題之前,我們要先來給大家介紹下PaaS化下幾個相關(guān)的名詞,及這些名詞背后的相互作用的關(guān)系。
可以先看下如下的架構(gòu)設(shè)計圖(筆者自己理解,與官方理解可能存在一定的差異):
??
相關(guān)名詞及對應(yīng)的業(yè)務(wù)含義簡要說明:
1)控制臺相關(guān):前臺或者中臺相關(guān)組織或角色在控制臺操作上傳前臺包,控制臺將相關(guān)的包信息寫入至相關(guān)存儲,并主動通知應(yīng)用系統(tǒng),應(yīng)用系統(tǒng)內(nèi)的Matrix SDK接收到通知后,執(zhí)行相關(guān)的熱更新及其他操作
2)Matrix SDK相關(guān):熱部署的包變更、發(fā)布等均依賴遠(yuǎn)端的控制臺操作,在控制臺操作審批通過后,遠(yuǎn)端的控制臺將信息變更寫入到相關(guān)存儲,并發(fā)送變更通知到各個docker應(yīng)用實例,各docker實例接受變更通知并執(zhí)行如熱部署等不同的操作。docker實例執(zhí)行熱部署的工作主要有:從遠(yuǎn)程私服下載jar到本地目錄,并執(zhí)行一系列的操作流程,包括但不限于前臺包卸載、前臺包更新等,其中前臺包更新包括但不限于:前臺包類加載、前臺包類初始化、前臺包生命周期管理、前臺包動態(tài)代理管理等。所謂萬丈高樓平地起,Matrix平臺框架的功能再復(fù)雜,實際的地基就是基于這一點(diǎn),并在基礎(chǔ)夯實疊加其他各類優(yōu)化和加強(qiáng)的功能,大家可以仔細(xì)領(lǐng)悟。
3)應(yīng)用層執(zhí)行相關(guān):中臺應(yīng)用層,在執(zhí)行至擴(kuò)展點(diǎn)相關(guān)的業(yè)務(wù)邏輯時,會動態(tài)代理至Matrix SDK,并由SDK接管邏輯,并確認(rèn)業(yè)務(wù)是否命中相關(guān)的業(yè)務(wù)身份,在在業(yè)務(wù)身份命中后執(zhí)行前臺包的實際邏輯。
4)監(jiān)控及安全相關(guān):控制臺及Matrix SDK相互合作,采集心跳、擴(kuò)展點(diǎn)執(zhí)行時間等各類統(tǒng)計維度數(shù)據(jù),此處也是一個較為復(fù)雜的體系,不詳細(xì)展開。
備注:以上架構(gòu)圖,只是筆者理解的Matrix技術(shù)框架的示意圖,僅供參考。
?
前臺包發(fā)布、熱部署及管理相關(guān)的主要的鏈路示意圖如下所示:
??
從圖中可以看到,我們的前臺包發(fā)布、熱部署存在“推”、“拉”兩條數(shù)據(jù)鏈路,兩條數(shù)據(jù)鏈路也分別適配不同的業(yè)務(wù)場景,如下詳細(xì)展開。
3.1.1、“推”鏈路
此鏈路的全過程可以表述為:軟件實施人員在控制臺,通過推送、生效等可視化界面工具操作完成后,控制臺會將用戶操作數(shù)據(jù)實時寫入至相關(guān)的數(shù)據(jù)中心,數(shù)據(jù)中心依賴的核心組件DUCC(一種類似ZK的存儲介質(zhì))會對外發(fā)布實時變更消息。集成了SDK的應(yīng)用容器收到DUCC變更消息通知后,感知控制臺的操作并依據(jù)操作類型不同做出差異化的反映:SDK依據(jù)操作的不同指令,在應(yīng)用容器中執(zhí)行不同的業(yè)務(wù)操作。
目前SDK中的操作策略指令包括但不限于如下4類:
1)推送:對應(yīng)的功能可表述為:SDK接收到指令后,從遠(yuǎn)端文件服務(wù)器將相關(guān)版本的前臺包下載至應(yīng)用容器的固定目錄中。此功能在Matrix技術(shù)框架中中對應(yīng)的處理類為:PublishActionImpl。
2)生效:對應(yīng)的功能可表述為:SDK接收到指令后,從遠(yuǎn)端文件服務(wù)器將相關(guān)版本的前臺包下載至應(yīng)用容器的固定目錄中,并執(zhí)行前臺包的熱加載功能(具體熱加載的功能說明,可以參考第二篇文章,此處不在贅述)。此功能在Matrix技術(shù)框架中中對應(yīng)的處理類為:TakeEffectActionImpl。
3)卸載:對應(yīng)的功能可表述為:SDK接收到指令后,將相關(guān)的前臺包從容器中卸載,并處理注銷相關(guān)的數(shù)據(jù)(備注:此功能在藏經(jīng)閣控制臺默認(rèn)情況下不會展示,我們平常情況下看不到這個功能菜單)。此功能在Matrix技術(shù)框架中中對應(yīng)的處理類為:UnloadActionImpl。
4)探活:對應(yīng)的功能可表述為:SDK接收到指令后,從當(dāng)前的容器服務(wù)器發(fā)送相關(guān)的心跳包,用以采集監(jiān)控當(dāng)前容器服務(wù)器相關(guān)的實時狀態(tài)數(shù)據(jù)。此功能在Matrix技術(shù)框架中中對應(yīng)的處理類為:DoAliveActionImpl。
“推”鏈路適用的場景為:應(yīng)用的場景為實時性要求較強(qiáng)的相關(guān)場景,包括但不限于新版本灰度及發(fā)布、版本回滾及控制、版本下線及監(jiān)控等。此類場景要求在控制臺執(zhí)行相關(guān)的操作步驟后,控制臺及相關(guān)的應(yīng)用容器在短時間內(nèi)(秒級或者毫秒級內(nèi))做出實時反映。主打的就是保障通知的及時性。
3.1.2、“拉”鏈路
此鏈路的全過程可以表述為:軟件實施人員,在相關(guān)的容器部署平臺,通過對容器進(jìn)行擴(kuò)容等操作,完成應(yīng)用擴(kuò)容。在對擴(kuò)容的機(jī)器進(jìn)行發(fā)布上線的時候,集成了SDK的應(yīng)用容器,會自動從數(shù)據(jù)中心獲取相關(guān)的應(yīng)用元數(shù)據(jù),在識別到元數(shù)據(jù)中存在部署版本的時候,自動從從遠(yuǎn)端文件服務(wù)器將最新生效版本的前臺包下載至應(yīng)用容器的固定目錄中,并執(zhí)行前臺包的熱加載功能。
此處需要注意一點(diǎn),在京東現(xiàn)有環(huán)境內(nèi),容器擴(kuò)容包含2步操作:1)部署平臺創(chuàng)建新的docker環(huán)境,完成應(yīng)用實例的創(chuàng)建,并處理復(fù)制更新好相關(guān)的鏡像環(huán)境;2)軟件實施人員對擴(kuò)容出來的應(yīng)用實例,執(zhí)行發(fā)布上線等操作。其中“拉”鏈路出現(xiàn)的時機(jī)出現(xiàn)在步驟二中。從邏輯代碼中,我們可以看到,在應(yīng)用系統(tǒng)發(fā)布上線后,在spring的生命周期內(nèi),Matrix會執(zhí)行相關(guān)“拉”邏輯。
“拉”鏈路適用的場景為:
1)對實時性要求不是很高的場景,如應(yīng)用擴(kuò)容等;
2)“推”鏈路無法觸達(dá)或觸達(dá)成本較高的場景。其中第二種場景有很多細(xì)分的業(yè)務(wù)場景,比如軟件實施人員對容器中的部分目錄文件執(zhí)行了誤刪除等操作,或者容器中的部分目錄文件出現(xiàn)了不可預(yù)知的損壞等異常場景。
?
3.1.3 、“推”和“拉”兩條鏈路設(shè)計理念說明
從上面可以看到,“推”和“拉”兩條鏈路都有其獨(dú)自適應(yīng)的應(yīng)用場景,那么筆者提出一個問題,是否有可能只保留其中的一條鏈路呢?讀者在不看后方的內(nèi)容時可提取思考一下,為什么Matrix框架會執(zhí)行如此設(shè)計呢?
在直接得出結(jié)論前,我們可以假定一下若只存在單鏈路,然后看看在單鏈路的情況下相關(guān)的復(fù)雜場景及其對應(yīng)的解決方案,最后我們再來綜合評定最優(yōu)方案。
1、只存在“推”單鏈路:
需要應(yīng)對的復(fù)雜場景:應(yīng)用擴(kuò)容。
復(fù)雜場景下需要解決的問題:在應(yīng)用擴(kuò)容時,在“推”鏈路下,數(shù)據(jù)中心如何能快速感知到擴(kuò)容容器的存在,同時對此類場景實時下發(fā)處理消息降低實施成本。
可選的解決方案:在應(yīng)用擴(kuò)容發(fā)布時,應(yīng)用端主動向數(shù)據(jù)中心發(fā)送相關(guān)的消息,獲取數(shù)據(jù)中心最新的應(yīng)用數(shù)據(jù)等;數(shù)據(jù)中心接收到消息后,再實時下發(fā)相關(guān)的處理消息。應(yīng)用容器接收到消息后,再執(zhí)行下載前臺包、熱更新等前臺包部署操作。
弊端:應(yīng)用擴(kuò)容發(fā)布后,按一般情況,應(yīng)用即可對外提供服務(wù)。為了防止出現(xiàn)應(yīng)用已對外提供了服務(wù),但是相關(guān)的前臺包還沒有拉取到應(yīng)用本地,導(dǎo)致相關(guān)的擴(kuò)展點(diǎn)出現(xiàn)“空轉(zhuǎn)”等情況。為了解決此問題,上方提及的可選的解決方案,務(wù)必要保障實時性特別高,即務(wù)必保障在系統(tǒng)啟動并對外提供服務(wù)前,快速完成解決方案。那么怎么保障?最好的辦法就是在未處理成功前,設(shè)定系統(tǒng)沒有啟動成功,不能對外提供服務(wù)。如此,此方案落地層面,涉及SDK和數(shù)據(jù)中心一來一回兩次交互,存在方案較為復(fù)雜,存在后續(xù)維護(hù)運(yùn)維成本高的問題;同時還存在消息鏈路太長,導(dǎo)致整個項目的啟動時長變得不可控,擴(kuò)容體驗變得極差的問題。
?
2、只存在“拉”單鏈路:
需要應(yīng)對的復(fù)雜場景:軟件實施人員在控制臺實時操作“發(fā)布”、“推包”等操作。
復(fù)雜場景下需要解決的問題:軟件實施人員執(zhí)行的操作,在“拉”鏈路下,在應(yīng)用端要可以及時感知到。
可選的解決方案:可選的方案有2個:1)控制臺和應(yīng)用端建立直接聯(lián)系,在控制臺完成相關(guān)操作后,直接通過聯(lián)系渠道將操作傳遞給應(yīng)用端;2)應(yīng)用端定時獲取數(shù)據(jù)中心的最新應(yīng)用狀態(tài)數(shù)據(jù)。但是方案一基本變相等同于是“推”鏈路,此處可以忽略不計。方案二理論上可行,但是也存在2個弊端:1)定時的時長不好控制,太長了,則操作可被感知具有一定的延遲性,存在客戶體驗不佳的情況;2)定時時長太短了,多個應(yīng)用無腦同時請求數(shù)據(jù)中心,對數(shù)據(jù)中心的高可用提出了極高的要求。對此弊端,我們可選的方案有:制定業(yè)務(wù)可接收的延時時間,并定時去請求數(shù)據(jù)中心。并對請求時,請求數(shù)據(jù)和返回數(shù)據(jù)存在數(shù)據(jù)過大的情況,采取數(shù)據(jù)極限裁剪、數(shù)據(jù)壓縮等方案。并設(shè)定獨(dú)立專業(yè)團(tuán)隊,通過建立雙數(shù)據(jù)中心等機(jī)制,保障數(shù)據(jù)中心的安全性及高可用性。
弊端:從根上來講,相關(guān)的通知鏈路總會存在或多或少的時延,客戶體驗或多或少都有一定的影響;同時方案復(fù)雜性變高,維護(hù)成本變大。
小結(jié):從上述兩處推測方案可以看到,單純只是采用一種解決方案,均存在較多的方案瑕疵。而將“推”和“拉”兩個方案進(jìn)行結(jié)合,并將方案應(yīng)用在其適應(yīng)的領(lǐng)域,則上述提及的弊端均可以以最小的成本來得以化解。而“推”、“拉”結(jié)合的解決方案,在業(yè)界的各類場景下,也存在廣泛運(yùn)用。大家可以多加領(lǐng)悟。
3.2、熱部署相關(guān)注意事項
1、需要注意控制大面積擴(kuò)容的節(jié)奏:從上述的表述中,我們可以看到,應(yīng)用在擴(kuò)容發(fā)布的時候,集成了SDK的應(yīng)用服務(wù)器,會從遠(yuǎn)端服務(wù)器拉取最新的前臺包至應(yīng)用服務(wù)器。假定我們的前臺包裁剪的大小比例不合理,一個前臺包的大小在300M及以上,加之我們擴(kuò)容采取的是大面積擴(kuò)容發(fā)布上線應(yīng)對線上緊急情況的場景,那么大促的某個瞬間,會存在同一時刻,應(yīng)用容器集中請求文件服務(wù)器,可能導(dǎo)致文件服務(wù)器過載,進(jìn)而導(dǎo)致下載失敗和發(fā)布上線啟動不成功的情況。這種情況,我是頂不住,不知道你頂不頂?shù)米。?/p>
對于這種情況,推薦3類解決方案:1)每次只進(jìn)行小批量擴(kuò)容,保障擴(kuò)容數(shù)量的合理性;2)仍然采取大批量擴(kuò)容,但在應(yīng)用發(fā)布啟動時采用小批量的方式分批進(jìn)行;3)在大面積擴(kuò)容時,通知Matrix相關(guān)同事,要求其對文件服務(wù)器及擴(kuò)容操作進(jìn)行重點(diǎn)保障。至于具體落地方案怎么選擇,大家可多加思考,并自由抉擇。
2、需要注意控制前臺包的大小:不管是“推”還是“拉”哪條鏈路,均會涉及到對前臺包從遠(yuǎn)端服務(wù)器下載至本地的操作。從感官上來,此項操作對網(wǎng)絡(luò)資源的消耗、應(yīng)用服務(wù)器本地資源的讀寫均會在瞬間造成較大的影響。如此,就要求我們的共建方前臺角色、中臺角色嚴(yán)格把控前臺包的大小,避免無關(guān)的包打入,控制前臺包的大小在相對合理的區(qū)間。
3.3、問題解答及分析
依據(jù)上方的講解,我們再來看下序言中提及的三個問題,來看看最終的答案,不知和你心中的答案是否一致呢?
問題1)使用前臺擴(kuò)展包,在發(fā)布平臺操作完成(完成推送、生效)后,應(yīng)用端進(jìn)行擴(kuò)容上線,擴(kuò)展點(diǎn)包是否可以自動拉取加載、自動掛載運(yùn)行?
答案:可以。此時應(yīng)用端擴(kuò)容上線,因為應(yīng)用服務(wù)器上不存在任何前臺包,會采取“拉”鏈路,將相關(guān)的前臺包一次性拉取到本地進(jìn)行加載、自動掛載運(yùn)行。
?
問題2)使用前臺擴(kuò)展包,在發(fā)布平臺操作完成(完成推送、生效)前,應(yīng)用端進(jìn)行擴(kuò)容上線,擴(kuò)展點(diǎn)包是否可以自動拉取加載、自動掛載運(yùn)行?
答案:可以。此時仍然是“拉”鏈路在生效,但是需要注意一點(diǎn),次數(shù)“拉”鏈路拉取的數(shù)據(jù),為發(fā)布平臺管理的上一次(如果有的話)上線的最新信息。在完成擴(kuò)容上線后,如果我們沒有在發(fā)布平臺對新擴(kuò)容的機(jī)器進(jìn)行推包、發(fā)布等操作,線上應(yīng)用會存在并運(yùn)行兩個版本的前臺包。
問題3)使用前臺擴(kuò)展包,在發(fā)布平臺對部分容器完成前臺包灰度推送,但沒有觸發(fā)生效,此時對這些容器執(zhí)行重啟操作,推送的前臺包是否會掛載運(yùn)行?
答案:不會。雖然說前臺包已完成灰度推送,相關(guān)的前臺包文件已在應(yīng)用容器中存在,但是容器執(zhí)行重啟操作時,SDK會自動按分組、IP等檢測最新已生效的版本,若發(fā)現(xiàn)當(dāng)前版本并沒有生效,哪怕這個前臺包文件在容器中已存在,也不會掛載運(yùn)行。
問題4)在測試環(huán)境亦或是其他環(huán)境,一不小心刪除了熱部署包路徑及對應(yīng)的文件,會有什么問題?如何解決?
答案:分情況。
情況1:若是我們將前臺包相關(guān)的目錄進(jìn)行了整個移除,則在應(yīng)用容器再次進(jìn)行啟動的時候,會執(zhí)行“拉”鏈路,將相關(guān)的前臺包一次性拉取到本地進(jìn)行加載、自動掛載運(yùn)行。
情況2:若是我們講前臺包相關(guān)的目錄中的部分文件進(jìn)行移除(具體移除了哪類文件此處不詳細(xì)展開),“拉”鏈路識別前臺包文件已存在,“拉”鏈路不會得以執(zhí)行。但是因為文件已損毀,相關(guān)的數(shù)據(jù)不完整,SDK在記載的過程中,會拋出相關(guān)的錯誤異常,并給出相關(guān)的錯誤信息,最終呈現(xiàn)的效果為整個應(yīng)用啟動失敗。
4、前中臺隔離原理
前文介紹的熱部署操作完畢后,前臺擴(kuò)展包,就可以正確在中臺相關(guān)的容器中完成部署了加載了。那么此處問題來了,前臺和中臺會共用某些相關(guān)的類,如何保障執(zhí)行的安全性及可靠性呢。那么就不得不提到了類隔離機(jī)制,和隔離機(jī)制背后的原理:雙親委派模型。
?
類隔離機(jī)制:前臺和中臺使用不同的ClassLoader來加載實現(xiàn)隔離,中臺使用默認(rèn)的ClassLoader,前臺使用自定義的ClassLoader,其中部分類為了避免沖突,前中臺共享了默認(rèn)的ClassLoader,這些類包括但不限于RPC框架、緩存框架等基礎(chǔ)組件包。
筆者早期工作的時候,在web應(yīng)用系統(tǒng),使用Eclipse的osgi概念在開發(fā)處理相關(guān)的bundle包的時候,對于涉及web應(yīng)用中登錄的session等復(fù)雜的數(shù)據(jù)結(jié)構(gòu)進(jìn)行信息傳遞處理的時候,經(jīng)常出現(xiàn)2類及以上的ClassLoader協(xié)同出現(xiàn)問題,導(dǎo)致應(yīng)用系統(tǒng)出現(xiàn)一些莫名的、難以排查的ClassNotFoundException。表象為應(yīng)用的工程包明明存在,在執(zhí)行的時候就是會出現(xiàn)錯誤的奇特場景。當(dāng)時初畢業(yè),技術(shù)功底淺薄,對此類問題印象極其深刻。而在我們在京東的實際使用的過程中,暫無發(fā)現(xiàn)此類問題。如果大家有遇到此類問題,歡迎進(jìn)一步交流。
?
說到類加載,就不得不提Java體系中大名鼎鼎的“雙親委派模型”(英文描述為:parents delegation model)。有關(guān)這個描述,此處多說一點(diǎn)。中文含義中的“雙”,其實在英文描述中并沒有對應(yīng)的描述,也即“雙”不“雙”,其實不緊要,緊要的是“委派”二字。“雙親委派模型”的核心內(nèi)容,用稍粗鄙點(diǎn)的言語可以表述為:有事找爸爸(爸爸如果也有事,可以找他爸爸的爸爸,以此類推)。此設(shè)計理念的好處內(nèi)外網(wǎng)相關(guān)文章介紹得較多,此處不在贅述。
??
此模型的簡要代碼可以表述如下:
protected Class??> loadClass(String name,boolean resolve) throwsClassNotFoundException { synchronized(getClassLoadingLock(name)){ // First, check if the class has already been loaded Class??> c =findLoadedClass(name); if(c ==null){ long t0 =System.nanoTime(); try{ if(parent !=null){ c = parent.loadClass(name,false); }else{ c =findBootstrapClassOrNull(name); } }catch(ClassNotFoundException e){ // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if(c ==null){ // If still not found, then invoke findClass in order // to find the class. long t1 =System.nanoTime(); c =findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if(resolve){ resolveClass(c); } return c; } }
所謂“雙親委派模型”,從上述代碼可以看到,先檢查類是否被加載過(第6行),若沒有加載則調(diào)用父加載器進(jìn)行加載(第11行),若父加載器為空則默認(rèn)使用啟動類加載器進(jìn)行加載(低13行)??梢钥吹?,若都失敗,則調(diào)用當(dāng)前類加載器進(jìn)行加載(第24行)。是不是和上述找“爸爸”的描述,比較契合?
在Matrix框架下,中臺相關(guān)的邏輯和前臺相關(guān)的業(yè)務(wù)包,是由2個獨(dú)立的ClassLoader類分開管理,其中前臺相關(guān)的業(yè)務(wù)包,是由名為com.jd.matrix.core.classloader.BizClassLoader的類進(jìn)行管理。我們先來看下這個類的具體邏輯:
public class BizClassLoader extends URLClassLoader{ public BizClassLoader(URL[] urls){ super(urls); } protected Class??>loadClass(String name,boolean resolve) throwsClassNotFoundException{ Class??> clazz =null; clazz =this.findLoadedClass(name); if(clazz !=null){ return clazz; }else{ try{ ClassLoader jdkClassLoader =ClassLoaderFactory.getJDKClassLoader(); clazz = jdkClassLoader.loadClass(name); if(clazz !=null){ return clazz; } }catch(ClassNotFoundException exception){ } if(ExportClassManager.match(name)){ clazz =ClassLoaderFactory.getInternalClassLoader().loadClass(name); if(clazz !=null){ return clazz; } } try{ clazz =this.findClass(name); }catch(ClassNotFoundException exception){ } if(clazz ==null){ clazz =ClassLoaderFactory.getInternalClassLoader().loadClass(name); } return clazz; } } protected Class??> findClass(String name) throws ClassNotFoundException{ returnsuper.findClass(name); }}
從代碼邏輯可以看到,Matrix提供的類加載器,其實是破壞了“雙親委派模型”,破壞點(diǎn)從13行開始,若當(dāng)前類沒有加載,沒有調(diào)用父加載器進(jìn)行加載,而是使用了jdkClassLoader去加載;同時若開啟了Matrix包屏蔽的策略(21行),則會使用中臺的ClassLoader去加載。
此處破壞的妙處在于:前臺包可以找到并使用中臺包對應(yīng)的相關(guān)類;但是中臺包不可以找到并使用前臺包對應(yīng)的相關(guān)類。粗俗點(diǎn)可以描述為:大哥不知道小弟,但是小弟在大哥的開放區(qū)域,可以有限了解大哥。
從此處也可以看到,Matrix對于類加載的管理與控制,與OSGI等標(biāo)準(zhǔn)規(guī)范相比,安全性等管控方面要松弛得多。從嚴(yán)格意義上來,此類管理的方式,安全性其實并不足,若中臺開放的區(qū)域過大,加上前臺包被有心人動手腳,其實是存在安全隱患,并防不勝防。
?
我們可以看到,Matrix會結(jié)合容器側(cè)的spring配置里的exportClassConfig屬性,使用自定義的類加載器(BizClassLoader)去加載垂直業(yè)務(wù)包里的類,主要有兩個核心內(nèi)容:
1、對于不同的前臺業(yè)務(wù)包里的類,分別使用BizClassLoader的不同實例去加載,對于垂直業(yè)務(wù)包里的自己開發(fā)的業(yè)務(wù)類,即使不同的垂直業(yè)務(wù)包中存在全限定名相同的類,但因為加載他們的BizClassLoader實例不同,不會出現(xiàn)沖突問題,類隔離的要求能夠得到滿足。
2、對于exportClassConfig這個屬性配置的相關(guān)類(包含但不限于:中臺提供的sdk包所包含的類、基礎(chǔ)RPC框架類),不會由1里所提及的自定義的classLoader的實例去加載,只會由同一個類加載器(加載中臺容器業(yè)務(wù)類的appClassLoader)去加載,以此來保證中臺容器調(diào)用擴(kuò)展點(diǎn)實現(xiàn)時參數(shù)類型校驗的一致性。
從上方的信息和代碼中,我們可以看到, 假定前臺和中臺存在相同包名、相同類名的類,在執(zhí)行前臺擴(kuò)展點(diǎn)的時候,分為前后2個步驟:1)執(zhí)行前臺包擴(kuò)展點(diǎn)的業(yè)務(wù)身份匹配邏輯;2)執(zhí)行前臺包擴(kuò)展點(diǎn)的實際業(yè)務(wù)邏輯。2者的區(qū)別,主要在于后者有一個主動切換當(dāng)前類加載器的邏輯。也即在步驟1時,當(dāng)前的類加載器為應(yīng)用容器業(yè)務(wù)類的加載器;步驟2時,當(dāng)前的類加載器切換為獨(dú)立的類加載器。
5、前臺業(yè)務(wù)身份設(shè)計原理
中臺和前臺合作的巧妙之處,在于中臺開放了多種標(biāo)準(zhǔn)的、可擴(kuò)展的能力,前臺基于自己的不同的業(yè)務(wù)身份實現(xiàn)多種個性化業(yè)務(wù)場景。即一種中臺的能力,可對應(yīng)多個前臺擴(kuò)展部署包。那么運(yùn)行時,中臺如何知道應(yīng)該使用哪些前臺包呢?
這里就不得不提業(yè)務(wù)身份這個概念了,在中臺的建設(shè)思想中,每個前臺包都有自己獨(dú)立的業(yè)務(wù)身份。中臺系統(tǒng)運(yùn)行的時候,依次匹配前臺部署包,并選擇業(yè)務(wù)身份命中的前臺包執(zhí)行相關(guān)邏輯。
?
業(yè)務(wù)身份的識別方式:Matrix業(yè)務(wù)身份有2種識別方式
1、前臺包手工編寫識別業(yè)務(wù)身份。即在前臺自己在元數(shù)據(jù)(Annotation)定義自己獨(dú)立的parserClass ,識別方式,由前臺業(yè)務(wù)系統(tǒng)手工編寫識別。這種方式的好處是純手寫代碼,加上前中臺組合review的時候,一般不會出現(xiàn)功能問題及性能瓶頸。但是也會存在很大一個弊端,即對某個業(yè)務(wù)而言,是不是屬于自己的業(yè)務(wù)身份,是前臺自己說了算,在一般情況下是沒有問題的,但是如果前中臺reivew的機(jī)制失效了,前臺基于某些特定的原因,將前臺的業(yè)務(wù)身份識別方式調(diào)整擴(kuò)大或縮小,會導(dǎo)致影響的業(yè)務(wù)范圍也會對應(yīng)的增大或者縮小,影響范圍非常不可控。
從中臺建設(shè)初期來看,本來預(yù)計這種方式,以后一定會廢棄掉。從過去幾年的實踐經(jīng)驗來看,這個地方預(yù)判錯了,這種形式目前仍然是主流的形式,只是不同的系統(tǒng)對同一個業(yè)務(wù)身份的識別,邏輯千差萬別,在串聯(lián)業(yè)務(wù)流程的過程,確實會存在較為痛苦的情況。
??
2、中臺統(tǒng)一管控識別業(yè)務(wù)身份。在在前臺自己在元數(shù)據(jù)(Annotation)設(shè)置autoParser的值為true,此時不用前臺來判斷處理和業(yè)務(wù)身份相關(guān)的控制邏輯了,這個邏輯會內(nèi)置在中臺,中臺相關(guān)的業(yè)務(wù)身份的解析類為AutoBizCodeParser,此類的定義與實現(xiàn)框架與前臺包定義的方式無異,只是實現(xiàn)了平臺通用的能力:即由中臺來決定是否可以命中某一個業(yè)務(wù)身份,在管理平臺增加相關(guān)配置來匹配是否可命中業(yè)務(wù)身份。
核心的邏輯是Matrix內(nèi)置定義了一個簡單的腳本模板,在系統(tǒng)采用熱部署指令,加載啟動前臺包的時候,識別到前臺包包含App元數(shù)據(jù)(Annotation)是自動識別業(yè)務(wù)身份的時候,會在系統(tǒng)內(nèi)按內(nèi)置的腳本模板采用javassist框架來初始化緩存一套字節(jié)碼。在實際去匹配前臺業(yè)務(wù)身份的時候,實際執(zhí)行的是此類字節(jié)碼來動態(tài)判斷業(yè)務(wù)身份是否可以命中。字節(jié)碼的模板代碼如下所示:
需要注意一點(diǎn),目前底層框架生成字節(jié)碼的時間點(diǎn),并不是系統(tǒng)自動加載或者熱部署生效的時候,而是嘗試命中業(yè)務(wù)邏輯的時候。預(yù)判肯定會存在執(zhí)行過程中第一次速度緩慢的情況,實際使用的時候可以多加注意。
#{ClassStart} import java.util.*; #{Package} public class #{ClassName} { #{MethodInfo} } #{ClassEnd} #{PackageStart} import #{TypeName}; #{PackageEnd} #{VarStart} #{Type} #{Var}=(#{Type})context.get("#{Var}"); #{VarEnd} #{executeStart} public boolean execute(Map context){ #{VarInfo} return #{Express}; } #{executeEnd} #{allStart} private boolean all(#{Var} ){ Iterator iterator =#{Collection}.iterator(); while (iterator.hasNext()) { #{ItemType} #{ItemVar}=(#{ItemType})iterator.next(); if(!(#{Right})){ return false; } } return true; } #{allEnd} #{anyStart} private boolean any(#{Var} ){ Iterator iterator = #{Collection}.iterator(); while (iterator.hasNext()) { #{ItemType} #{ItemVar}=(#{ItemType})iterator.next(); if((#{Right})){ return true; } } return false; } #{anyEnd}
業(yè)務(wù)身份命中基本流程:此處其實無需多言,業(yè)務(wù)身份命中的邏輯其實隱含在前面介紹的前臺包業(yè)務(wù)邏輯實際執(zhí)行的地方,只是Matrix框架在執(zhí)行邏輯前,創(chuàng)造了一個類似會話(Session)的概念,在會話中去對業(yè)務(wù)身份是否命中執(zhí)行相關(guān)匹配邏輯,實際就是對相關(guān)的前臺業(yè)務(wù)包執(zhí)行fiter方法,判斷評估業(yè)務(wù)身份命中。在會話(Session)中對前臺包依次執(zhí)行filter去匹配業(yè)務(wù)身份。
存在的風(fēng)險:
1)部分前臺包業(yè)務(wù)身份識別邏輯較重,會導(dǎo)致整體中臺邏輯運(yùn)行緩慢,存在性能風(fēng)險;對應(yīng)的建議解決方案:業(yè)務(wù)身份識別邏輯一定要輕量級;
2)目前Matrix框架默認(rèn)仍然只是捕獲了Exception類別的異常,對于Throwable級別的異常仍然是沒有捕獲的。部分前臺包業(yè)務(wù)邏輯存在偶發(fā)性問題,執(zhí)行業(yè)務(wù)身份匹配filter邏輯時,若拋出Throwable級別的異常,會導(dǎo)致其余業(yè)務(wù)身份的前臺包也無法執(zhí)行。這真是一粒老鼠屎,搞壞一鍋粥。
?
業(yè)務(wù)身份基本原理:Matrix實際以業(yè)務(wù)身份來管理業(yè)務(wù)包與擴(kuò)展點(diǎn),同一個業(yè)務(wù)身份,可以命中一個垂直擴(kuò)展點(diǎn)(代號為Y)和一些水平擴(kuò)展點(diǎn)(代號為X)。針對同一個待識別的業(yè)務(wù)規(guī)則,比如同一個sku或者同一個訂單而言,不可以命中多個業(yè)務(wù)身份,若實際命中了多個業(yè)務(wù)身份,Matrix會限制只會命中其中的某一個業(yè)務(wù)身份對應(yīng)的擴(kuò)展點(diǎn)方法。也即對這種業(yè)務(wù)場景,Matrix即使會命中多個業(yè)務(wù)身份的fiter方法,但是實際只會執(zhí)行其中唯一的一個業(yè)務(wù)身份對應(yīng)的擴(kuò)展點(diǎn)方法,具體命中的業(yè)務(wù)身份,初看有一定的隨機(jī)性,具體順序參加下方的描述。
業(yè)務(wù)身份的順序:從Matrix遍歷業(yè)務(wù)身份處理擴(kuò)展點(diǎn)的業(yè)務(wù)邏輯來看,業(yè)務(wù)身份的順序至關(guān)重要,排名靠前的業(yè)務(wù)身份,其對應(yīng)的擴(kuò)展點(diǎn)方法執(zhí)行的幾率會大于排期靠后的業(yè)務(wù)身份。經(jīng)排查,Matrix對業(yè)務(wù)身份排名的邏輯由業(yè)務(wù)身份(App)的三個屬性來共同確定,具體為:priority(由大到?。ersion(由大到?。?、code(由大到?。?。在滿足上述排序規(guī)則的前提下確定業(yè)務(wù)身份的優(yōu)先級。假定業(yè)務(wù)上存在如下場景:對同一個訂單或者同一個sku的場景,同時可命中二級業(yè)務(wù)身份和三級業(yè)務(wù)身份,理論上業(yè)務(wù)期望三級業(yè)務(wù)身份被命中到。但是很不巧,priority、versionSpec等值設(shè)置不合理,導(dǎo)致二級業(yè)務(wù)身份被命中到出現(xiàn)與預(yù)期不一致的情況。對于此類場景,我們就需要制定嚴(yán)格的代碼規(guī)范,對前臺業(yè)務(wù)側(cè)限定其priority、version、code的數(shù)值,確保業(yè)務(wù)邏輯的正確性。
重點(diǎn)提示:業(yè)務(wù)身份的定義與使用,是業(yè)務(wù)系統(tǒng)出現(xiàn)問題最多的地方。業(yè)務(wù)身份在Matrix中是一個很重要的概念,其設(shè)計理念期待整個業(yè)務(wù)身份是一顆節(jié)點(diǎn)互不交叉的樹,但是在實際業(yè)務(wù)執(zhí)行的時候,經(jīng)常會存在業(yè)務(wù)身份重疊的情況。設(shè)計定義前臺包的人員,屬于兩個不同的部門,屬于兩撥不同的人,2者很難意識到業(yè)務(wù)存在交叉重疊的情況,已經(jīng)發(fā)生多次線上業(yè)務(wù)跑飛了出現(xiàn)與預(yù)期不一致的情況,后來花了大力氣來解決的場景。常見出現(xiàn)問題的案例舉例供大家參考:我們提供了一個擴(kuò)展點(diǎn)供前臺業(yè)務(wù)使用,其中一個前臺包的業(yè)務(wù)身份是大家電,另外一個前臺包的業(yè)務(wù)身份是五星。結(jié)果在實際業(yè)務(wù)上,同一個sku或者同一個訂單,既是大家電的業(yè)務(wù),也是五星的業(yè)務(wù)。對于這種場景,如何解決呢?這個問題留給大家來思考。
Matrix對業(yè)務(wù)身份排名的具體代碼如下所示,代碼邏輯參見BizCodeSpec.compareTo方法:
public int compareTo(BizCodeSpec o) { if (o == null) { return -1; } else if (this.priority != null && o.priority != null) { int ret = o.priority - this.priority; if (ret != 0) { return ret; } else if (this.versionSpec != null && o.versionSpec != null) { ret = this.versionSpec.compareTo(o.versionSpec); if (ret == 0) { ret = o.bizCode.compareTo(this.bizCode); } return ret; } else { return 0; } } else { return 0; } }
最佳實踐:
1、一般而言,我們在設(shè)計并編寫前臺包的時候,包名應(yīng)該具有前臺獨(dú)立的業(yè)務(wù)屬性,也即包名和中臺的包名應(yīng)該有一定的區(qū)隔。對于可能會和中臺包沖突的業(yè)務(wù)場景,我們可以在框架中進(jìn)行類名或者包名的排除。
2、中臺開放給前臺的包名范圍,應(yīng)該盡量限定在最小范圍,防止前臺不經(jīng)意的惡意行為。
除這2點(diǎn)外,可能還存在其他各類場景對應(yīng)的最佳實踐,有待我們進(jìn)一步探索。
6、思考
不管是中臺化也好,還是去中臺也好。核心思路,都在于如何以技術(shù),服務(wù)好我們的業(yè)務(wù)。中臺化,亦或者是品類差異化思路,均為交付過程中選擇使用的差異化工具。但是工具背后,必然隱藏著對應(yīng)的落地技術(shù)關(guān)鍵點(diǎn),而這些關(guān)鍵點(diǎn),道理都是相通的。作為技術(shù)人員,持續(xù)思考,如何從漫天繁花中,找到底層最核心的癥結(jié),不斷打磨豐富我們的產(chǎn)品,提供優(yōu)質(zhì)的服務(wù),值得我們持續(xù)探索。
審核編輯 黃宇
-
PaaS
+關(guān)注
關(guān)注
2文章
134瀏覽量
22051 -
SDK
+關(guān)注
關(guān)注
3文章
1058瀏覽量
47438 -
京東
+關(guān)注
關(guān)注
2文章
1013瀏覽量
49097
發(fā)布評論請先 登錄
數(shù)據(jù)中臺可以解決哪些問題
MES系統(tǒng)為什么需要數(shù)據(jù)中臺

電動汽車框架焊接中的電阻焊技術(shù)應(yīng)用探析
底層開發(fā)與應(yīng)用開發(fā)到底怎么選?
數(shù)字化車間數(shù)據(jù)中臺的功能作用及應(yīng)用場景
數(shù)字化車間中,如何有效實施數(shù)據(jù)中臺?
SSM框架的優(yōu)缺點(diǎn)分析 SSM在移動端開發(fā)中的應(yīng)用
模塊化示波器的技術(shù)原理和應(yīng)用
模塊化儀器的技術(shù)原理和應(yīng)用場景
云計算中的虛擬化技術(shù)應(yīng)用
京東金融APP的鴻蒙之旅系列專題 鴻蒙工程化:Hvigor構(gòu)建技術(shù)

京東金融APP的鴻蒙之旅系列專題 新特性篇:意圖框架接入

揭秘動態(tài)化跨端框架在鴻蒙系統(tǒng)下的高性能解決方案

評論