不要寫(xiě)死!天貓App的動(dòng)態(tài)化配置中心實(shí)踐

2016-03-15? 高嘉峻 ? 前端之巔

不要寫(xiě)死,一個(gè)永恒的話題。動(dòng)態(tài)化,也是一個(gè)涵蓋了界面,功能,數(shù)據(jù),配置諸多方面的一個(gè)寬泛話題。

在之前的一篇《解耦神器 —— 統(tǒng)跳協(xié)議和Rewrite引擎》(http://pingguohe.net/2015/11/24/Navigator-and-Rewrite.html)中提到了我們的配置中心。這篇文章就跟大家聊一聊手機(jī)天貓?jiān)?b>配置動(dòng)態(tài)化上的心路歷程。

動(dòng)態(tài)化就像野心一樣會(huì)膨脹

最初移動(dòng)App就是一個(gè)老老實(shí)實(shí)的App,一切都硬編碼在客戶端,只有業(yè)務(wù)數(shù)據(jù)是從API而來(lái)。

漸漸的就會(huì)發(fā)現(xiàn),這里的Tab文案需要?jiǎng)討B(tài)調(diào)整,那里的文本顏色需要個(gè)性定制。于是我們就在數(shù)據(jù)接口里加上一些信息來(lái)控制客戶端邏輯。

當(dāng)動(dòng)態(tài)調(diào)整發(fā)揮作用,我們就發(fā)現(xiàn)這玩意兒簡(jiǎn)直太棒了,客戶端邏輯居然也不需要版本依賴了!于是更多的邏輯開(kāi)始未雨綢繆,開(kāi)始在各自數(shù)據(jù)接口里預(yù)先埋下控制信息。

那些沒(méi)有與API數(shù)據(jù)交互邏輯的靜態(tài)功能就很不爽,天生的純客戶端邏輯,難道就活該不能動(dòng)態(tài)化?于是開(kāi)始考慮專門(mén)為動(dòng)態(tài)化去設(shè)計(jì)一個(gè)API,專門(mén)傳輸控制信息。

后來(lái)還在后端還做了一個(gè)可視化的編輯界面,管理這些專門(mén)的控制信息,雖然簡(jiǎn)陋了一點(diǎn),也比修改那些混在數(shù)據(jù)API里的信息要方便的多。

當(dāng)專門(mén)用來(lái)傳輸控制信息的API和管理界面投入使用,漸漸的那些原本混在數(shù)據(jù)API里的控制信息也加了進(jìn)來(lái) —— 一個(gè)叫做配置的功能就這么誕生了。

天貓?jiān)谂渲眠@件事上投入了很多,成長(zhǎng)至今,我們的配置中心也經(jīng)歷了漫長(zhǎng)的成長(zhǎng)過(guò)程,現(xiàn)在配置中心是一個(gè)支撐多款應(yīng)用多個(gè)平臺(tái)超過(guò)150個(gè)業(yè)務(wù)模塊的系統(tǒng)。在這個(gè)配置中心里,有大到超過(guò)100條跳轉(zhuǎn)規(guī)則的Rewrite引擎,小到僅僅配置了一個(gè)狀態(tài)位的緩存開(kāi)關(guān);強(qiáng)業(yè)務(wù)相關(guān)的頭圖鏈接,強(qiáng)技術(shù)性的安全模式級(jí)別;最前端的App主題風(fēng)格,最后端的日志打印策略;等等。

最簡(jiǎn)陋的配置文件

手機(jī)天貓第一個(gè)可以被稱為配置中心的系統(tǒng)并非一個(gè)獨(dú)立運(yùn)行的應(yīng)用,而是寄生在一個(gè)被稱為T(mén)MS的類似CMS的前端平臺(tái)上。最初的配置是每個(gè)業(yè)務(wù)在這個(gè)TMS上創(chuàng)建一張頁(yè)面,以JSON方式輸出到CDN,客戶端代碼按需下載這份JSON文件,從而實(shí)現(xiàn)動(dòng)態(tài)化。逐漸我們發(fā)現(xiàn),類似的需求越來(lái)越多,多個(gè)業(yè)務(wù)都在以這樣的方式做動(dòng)態(tài)化。

許多人在重復(fù)做同一件事,那么這件事就一定值得抽象。基于這樣的邏輯,我們抽象了一個(gè)可以支撐多個(gè)業(yè)務(wù)的“配置中心”,同時(shí)把功能從TMS遷移到了另一個(gè)更輕量的前端平臺(tái)——TWP上。在這個(gè)平臺(tái)上,所有模塊的配置都被描述為一張二維表,每一個(gè)模塊可以在模版中訂制列,而每一行就是該模塊下的一條配置信息。每一次配置信息發(fā)生變更,這張二維表的數(shù)據(jù)會(huì)被提交到模版中的每個(gè)模塊訂制的一個(gè)JS函數(shù),這個(gè)JS會(huì)針對(duì)二維表提交來(lái)的數(shù)據(jù)做校驗(yàn)和簡(jiǎn)單的格式化。

這個(gè)配置中心通過(guò)互相配合的兩張頁(yè)面模版最終生成一份被存儲(chǔ)在CDN的配置文件。

一張頁(yè)面做“模塊配置”,用來(lái)管理接入配置中心的模塊信息。包括:模塊名稱,列配置。

另一張頁(yè)面做“數(shù)據(jù)配置”,通過(guò)讀取“模塊配置”的信息生成一個(gè)支持多sheet的二位表簇,同時(shí)還持有每一張二維表,也就是每一個(gè)模塊,提交后的一段格式化JS函數(shù)。

每一次在“數(shù)據(jù)配置”頁(yè)面填寫(xiě)數(shù)據(jù)并保存后,二維表簇的全部數(shù)據(jù)會(huì)被提交到格式化函數(shù)中,所有模塊的JS函數(shù)會(huì)各自執(zhí)行,并把結(jié)果拼裝成一段完整的JSON,輸出到CDN上。

在這個(gè)簡(jiǎn)陋的配置中心里,我們最多支持了50-60個(gè)模塊的配置數(shù)據(jù),數(shù)據(jù)量也超過(guò)了50K。由于配置數(shù)據(jù)是靜態(tài)的,而且無(wú)法增量更新,客戶端的流量問(wèn)題非常嚴(yán)重。我們不得不再通過(guò)一個(gè)簡(jiǎn)單的Web應(yīng)用,通過(guò)讀取配置數(shù)據(jù)中的一個(gè)版本號(hào)(上圖,也是人肉配置的,需要每個(gè)修改配置的人修改這個(gè)版本號(hào)加1)比對(duì)客戶端上行的本地版本號(hào),來(lái)判斷是否有配置變更,從而決定是否讀取這份完整的配置文件并下發(fā)到客戶端。

橫向擴(kuò)展的配置中心

需求在膨脹

接入的模塊越來(lái)越多,配置文件尺寸爆炸,客戶端難以承受

線上版本增加,出現(xiàn)需要分平臺(tái)分版本投放的情況

業(yè)務(wù)復(fù)雜性上升,二維表無(wú)法滿足業(yè)務(wù)需求

新的業(yè)務(wù)需求推動(dòng)配置中心的一次大重構(gòu),開(kāi)始重新梳理配置信息的數(shù)據(jù)結(jié)構(gòu),并建設(shè)獨(dú)立應(yīng)用。

新配置中心

配置中心的數(shù)據(jù)結(jié)構(gòu)必須兼顧統(tǒng)一性個(gè)性化。統(tǒng)一的數(shù)據(jù)結(jié)構(gòu)是抽象管理邏輯的基礎(chǔ),針對(duì)統(tǒng)一的數(shù)據(jù)結(jié)構(gòu)可以設(shè)計(jì)出通用的管理界面,存儲(chǔ)邏輯;而具有一定的個(gè)性化能力才能針對(duì)不同的場(chǎng)景更好的滿足業(yè)務(wù)需求。

模型

基于這樣的考慮,我們?cè)O(shè)計(jì)了Key-List-Object的三層數(shù)據(jù)結(jié)構(gòu)。也就是說(shuō),整體上配置數(shù)據(jù)是一個(gè)Map,Map中的每一項(xiàng)都是一個(gè)List,而List中的項(xiàng)則不規(guī)定結(jié)構(gòu),可以自由發(fā)揮。Key-List-Object結(jié)構(gòu)對(duì)應(yīng)到業(yè)務(wù)上:

Key - 表示模塊名

List - 表示模塊數(shù)據(jù)

Object - 表示模塊中的一條配置項(xiàng)

配置項(xiàng),也就是Object是整個(gè)數(shù)據(jù)模型中的原子。

此外,為了支持跨平臺(tái)和版本的需求,還設(shè)計(jì)了應(yīng)用-平臺(tái)-版本-模塊的元信息模型。

應(yīng)用 - 這個(gè)系統(tǒng)可以支撐多款應(yīng)用

平臺(tái) - 如:iPhone,Android Phone,iPad,Android Pad

版本 - 就是App的軟件版本

模塊 - 對(duì)應(yīng)到數(shù)據(jù)結(jié)構(gòu)的Key上

元信息模型和數(shù)據(jù)模型結(jié)合起來(lái)組成了完整的配置中心數(shù)據(jù)模型。數(shù)據(jù)項(xiàng)直接關(guān)聯(lián)應(yīng)用,模塊和版本號(hào)三個(gè)元信息,版本號(hào)關(guān)聯(lián)平臺(tái),模塊關(guān)聯(lián)應(yīng)用。在這樣的關(guān)聯(lián)下,任意一個(gè)請(qǐng)求到達(dá)后:

提取請(qǐng)求元信息:應(yīng)用,平臺(tái),版本號(hào)

根據(jù)提取的應(yīng)用找到關(guān)聯(lián)的模塊列表

根據(jù)平臺(tái),版本,模塊獲取全部相關(guān)配置項(xiàng)

把全部構(gòu)建成Key-List-Object的結(jié)構(gòu)返回

強(qiáng)大的橫向能力

在這樣的技術(shù)方案下,這個(gè)新配置中心可以滿足幾乎全部橫向需求,支撐多個(gè)應(yīng)用,多平臺(tái),多版本同時(shí)管理。

修改某一個(gè)配置項(xiàng)并在多個(gè)版本同時(shí)生效,這樣的操作非常常見(jiàn),而在新技術(shù)方案下,給每一個(gè)版本的App都分配了獨(dú)立的配置項(xiàng)數(shù)據(jù)。為了解決這個(gè)問(wèn)題,我們給所有有關(guān)的配置項(xiàng)設(shè)計(jì)了一個(gè)獨(dú)立的關(guān)系,當(dāng)對(duì)某一個(gè)版本中的某一個(gè)配置項(xiàng)進(jìn)行查詢,同時(shí)可以查詢到與之關(guān)聯(lián)的其他版本中的項(xiàng)。修改某一個(gè)配置項(xiàng)也可以同步到同一個(gè)關(guān)系下的其他關(guān)聯(lián)項(xiàng)。通過(guò)關(guān)系解決了對(duì)配置信息的關(guān)聯(lián)處理。

全個(gè)性化的配置項(xiàng)

以上的模型滿足了統(tǒng)一性,而個(gè)性化則在配置項(xiàng)上體現(xiàn)。在Key-List-Object結(jié)構(gòu)中配置項(xiàng)沒(méi)有硬性的格式要求,所以每一個(gè)項(xiàng)都可以有其個(gè)性結(jié)構(gòu),只需要滿足JSON Schema的要求即可。

這樣的設(shè)計(jì)也可以保證對(duì)List沒(méi)有需求的場(chǎng)景,可以在首個(gè)配置項(xiàng)中有最高程度的設(shè)計(jì)自由度,定義適合業(yè)務(wù)的結(jié)構(gòu)。

增量更新

之前說(shuō)到新配置中心還要解決配置數(shù)據(jù)尺寸膨脹的問(wèn)題。而隨著業(yè)務(wù)增長(zhǎng),配置中心所支撐的業(yè)務(wù)模塊已經(jīng)超過(guò)了150個(gè)。在這樣的體量下,一份完整的配置文件已經(jīng)接近200KB。顯然,每次都加載這樣的一個(gè)文件是不現(xiàn)實(shí)的,因此我們開(kāi)發(fā)了針對(duì)模塊級(jí)別的增量更新功能。

任意一個(gè)模塊發(fā)生數(shù)據(jù)更新后,將向系統(tǒng)提交一次版本變更請(qǐng)求,整個(gè)配置版本號(hào)加1。配置中心記錄了每一次版本變更的模塊信息。當(dāng)數(shù)據(jù)請(qǐng)求到達(dá)配置中心后,系統(tǒng)將比對(duì)當(dāng)前最新版本和請(qǐng)求上行的App本地版本,并計(jì)算出版本差之間發(fā)生變更的模塊數(shù)據(jù),構(gòu)造一次增量數(shù)據(jù)返回。

在全部模塊中,最大的一個(gè)模塊數(shù)據(jù)不超過(guò)20KB,頻繁更新的模塊數(shù)量不多于10個(gè),增量功能節(jié)約客戶端數(shù)據(jù)流量超過(guò)90%。

縱向延伸的信息通道

至此一個(gè)功能完善的配置中心初具規(guī)模,然而隨著配置中心接入的業(yè)務(wù)的增長(zhǎng),一個(gè)新的課題擺在我們面前。配置中心已經(jīng)托管了幾乎整個(gè)App的配置信息,系統(tǒng)的橫向擴(kuò)展只能積累量變,要進(jìn)一步刺激業(yè)務(wù)能力升級(jí)就需要配置中心縱向擴(kuò)展,在靜態(tài)配置之外的領(lǐng)域具備更強(qiáng)的功能。

縱向能力擴(kuò)展的思路也有不同。發(fā)展配置中心系統(tǒng)本身,針對(duì)需求擴(kuò)展外延是傳統(tǒng)思路。然而我們認(rèn)為這樣的做法不能最大限度的發(fā)揮配置中心所具備的的能力,資源產(chǎn)出比太低。如果可以調(diào)動(dòng)既有的其他系統(tǒng)的,推動(dòng)既有系統(tǒng)的無(wú)線化,發(fā)揮配置中心的通道能力,就可以調(diào)動(dòng)前無(wú)線時(shí)代我們積累的大量成熟系統(tǒng)能力,讓這些能力迅速在無(wú)線平臺(tái)上發(fā)揮作用。這種做法不僅充分?jǐn)U展了配置中心的縱向能力,又推動(dòng)了既有系統(tǒng)的無(wú)線化升級(jí)轉(zhuǎn)型,事半功倍。

對(duì)配置中心的縱向改造,我們從三個(gè)方面入手。

全開(kāi)放的服務(wù)接口

依據(jù)所承擔(dān)的功能,把配置中心拆分成網(wǎng)關(guān)、核心服務(wù)和界面系統(tǒng)三個(gè)部分,并對(duì)其進(jìn)行服務(wù)化改造。每一部分都具備獨(dú)立提供服務(wù)的能力,而他們之間的配合也通過(guò)服務(wù)化的接口來(lái)實(shí)現(xiàn)。

如此一來(lái),每一個(gè)部分所具備的能力都都可以提供給配置中心系統(tǒng)以外的其他系統(tǒng),而且拆分后的系統(tǒng),每一部分的發(fā)布頻率,系統(tǒng)要求,應(yīng)用特點(diǎn)各有不同,可以針對(duì)性更強(qiáng)的進(jìn)行系統(tǒng)優(yōu)化。

提升數(shù)據(jù)通道穩(wěn)定性

舊的配置中心設(shè)計(jì)中,網(wǎng)關(guān)直接返回核心系統(tǒng)計(jì)算出的配置數(shù)據(jù),只是通過(guò)內(nèi)存緩存來(lái)提升訪問(wèn)效率和穩(wěn)定性。而新的設(shè)計(jì)下配置中心將為更多業(yè)務(wù)提供數(shù)據(jù)通道服務(wù),必須選擇更安全可靠的通道方案。

我們決定把靜態(tài)配置數(shù)據(jù)按照業(yè)務(wù)切分,以橫向?qū)傩越M合為標(biāo)示,投放到CDN上。通過(guò)網(wǎng)關(guān)下發(fā)CDN鏈接,App端SDK拿到鏈接后自行下載數(shù)據(jù)。從而降低核心模塊的壓力,提升穩(wěn)定性。

高可定制的操作界面

此前,配置中心的界面趨于同質(zhì)化,誠(chéng)實(shí)的還原了Key-List-Object的數(shù)據(jù)結(jié)構(gòu),而配置項(xiàng)的編輯界面則是一個(gè)標(biāo)準(zhǔn)的JSON Editor。為了提升可用性,我們獨(dú)立設(shè)計(jì)界面系統(tǒng)。

在這個(gè)界面系統(tǒng)中,業(yè)務(wù)方可以使用標(biāo)準(zhǔn)的JSON

Schema來(lái)定義一張操作界面,也可以通過(guò)撰寫(xiě)HTML模版實(shí)現(xiàn)個(gè)性化更強(qiáng)的界面。界面的表單數(shù)據(jù)將被提交到一個(gè)負(fù)責(zé)格式化和校驗(yàn)的JavaScript方法中。在這個(gè)方法中表單數(shù)據(jù)被處理成系統(tǒng)所需要的配置數(shù)據(jù),投放給核心服務(wù)。

不要寫(xiě)死,永恒的話題

不要寫(xiě)死,一個(gè)永恒的話題,這個(gè)話題會(huì)一直持續(xù)下去。而動(dòng)態(tài)性這件事,是移動(dòng)設(shè)備App當(dāng)下最熱門(mén)的話題。在PC時(shí)代,我們的系統(tǒng)經(jīng)歷了C/S到B/S的轉(zhuǎn)換,終于實(shí)現(xiàn)了最大程度的動(dòng)態(tài)化。而在無(wú)線時(shí)代,移動(dòng)設(shè)備有他獨(dú)特的屬性,B/S模式無(wú)法滿足無(wú)線時(shí)代的業(yè)務(wù)需求,至少當(dāng)下是這樣。那么Native動(dòng)態(tài)化這條路,就還需要我們堅(jiān)定的走下去,這條路的盡頭可能是另一個(gè)B/S模式,也可能我們找到了完美的Dynamic

Wireless C/S模式。

各位朋友好,『移動(dòng)開(kāi)發(fā)前線』第一波福利活動(dòng):關(guān)注送書(shū)活動(dòng)已經(jīng)順利結(jié)束,獲獎(jiǎng)名單已經(jīng)出來(lái),現(xiàn)在向大家公布!~

本次活動(dòng)由人民郵電出版社贊助,送出10本最新最火的移動(dòng)開(kāi)發(fā)領(lǐng)域好書(shū)?;顒?dòng)參與者共303名,從中隨機(jī)抽出10名,他們的名單和選擇的書(shū)籍如下:

恭喜你們!我們已經(jīng)聯(lián)系出版社,稍后出版社會(huì)陸續(xù)將書(shū)送出。

本周四晚8點(diǎn)移動(dòng)前線群分享激情繼續(xù)!加群可參與提問(wèn)交流。

分享話題:天貓無(wú)線A/B測(cè)試實(shí)踐

分享嘉賓:高嘉峻(gaosboy)

加群方法:關(guān)注移動(dòng)開(kāi)發(fā)前線公眾號(hào),點(diǎn)擊『菜單加群』學(xué)習(xí),按提示操作。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容