1. 程式人生 > >不要寫死!天貓App的動態化配置中心實踐

不要寫死!天貓App的動態化配置中心實踐

不要寫死,一個永恆的話題。動態化,也是一個涵蓋了介面,功能,資料,配置諸多方面的一個寬泛話題。


在之前的一篇《解耦神器 —— 統跳協議和Rewrite引擎》(http://pingguohe.net/2015/11/24/Navigator-and-Rewrite.html)中提到了我們的配置中心。這篇文章就跟大家聊一聊手機天貓在配置動態化上的心路歷程。

動態化就像野心一樣會膨脹

最初移動App就是一個老老實實的App,一切都硬編碼在客戶端,只有業務資料是從API而來。

漸漸的就會發現,這裡的Tab文案需要動態調整,那裡的文字顏色需要個性定製。於是我們就在資料介面里加上一些資訊來控制客戶端邏輯。

當動態調整發揮作用,我們就發現這玩意兒簡直太棒了,客戶端邏輯居然也不需要版本依賴了!於是更多的邏輯開始未雨綢繆,開始在各自資料介面裡預先埋下控制資訊。

那些沒有與API資料互動邏輯的靜態功能就很不爽,天生的純客戶端邏輯,難道就活該不能動態化?於是開始考慮專門為動態化去設計一個API,專門傳輸控制資訊。

後來還在後端還做了一個視覺化的編輯介面,管理這些專門的控制資訊,雖然簡陋了一點,也比修改那些混在資料API裡的資訊要方便的多。

當專門用來傳輸控制資訊的API和管理介面投入使用,漸漸的那些原本混在資料API裡的控制資訊也加了進來 —— 一個叫做配置的功能就這麼誕生了。

天貓在配置這件事上投入了很多,成長至今,我們的配置中心也經歷了漫長的成長過程,現在配置中心是一個支撐多款應用多個平臺超過150個業務模組的系統。在這個配置中心裡,有大到超過100條跳轉規則的Rewrite引擎,小到僅僅配置了一個狀態位的快取開關;強業務相關的頭圖連結,強技術性的安全模式級別;最前端的App主題風格,最後端的日誌列印策略;等等。

最簡陋的配置檔案

手機天貓第一個可以被稱為配置中心的系統並非一個獨立執行的應用,而是寄生在一個被稱為TMS的類似CMS的前端平臺上。最初的配置是每個業務在這個TMS上建立一張頁面,以JSON方式輸出到CDN,客戶端程式碼按需下載這份JSON檔案,從而實現動態化。逐漸我們發現,類似的需求越來越多,多個業務都在以這樣的方式做動態化。

許多人在重複做同一件事,那麼這件事就一定值得抽象。基於這樣的邏輯,我們抽象了一個可以支撐多個業務的“配置中心”,同時把功能從TMS遷移到了另一個更輕量的前端平臺——TWP上。在這個平臺上,所有模組的配置都被描述為一張二維表,每一個模組可以在模版中訂製列,而每一行就是該模組下的一條配置資訊。每一次配置資訊發生變更,這張二維表的資料會被提交到模版中的每個模組訂製的一個JS函式,這個JS會針對二維表提交來的資料做校驗和簡單的格式化。

這個配置中心通過互相配合的兩張頁面模版最終生成一份被儲存在CDN的配置檔案。

  • 一張頁面做“模組配置”,用來管理接入配置中心的模組資訊。包括:模組名稱,列配置。

  • 另一張頁面做“資料配置”,通過讀取“模組配置”的資訊生成一個支援多sheet的二位表簇,同時還持有每一張二維表,也就是每一個模組,提交後的一段格式化JS函式。

每一次在“資料配置”頁面填寫資料並儲存後,二維表簇的全部資料會被提交到格式化函式中,所有模組的JS函式會各自執行,並把結果拼裝成一段完整的JSON,輸出到CDN上。

在這個簡陋的配置中心裡,我們最多支援了50-60個模組的配置資料,資料量也超過了50K。由於配置資料是靜態的,而且無法增量更新,客戶端的流量問題非常嚴重。我們不得不再通過一個簡單的Web應用,通過讀取配置資料中的一個版本號(上圖,也是人肉配置的,需要每個修改配置的人修改這個版本號加1)比對客戶端上行的本地版本號,來判斷是否有配置變更,從而決定是否讀取這份完整的配置檔案並下發到客戶端。

橫向擴充套件的配置中心

需求在膨脹

  • 接入的模組越來越多,配置檔案尺寸爆炸,客戶端難以承受

  • 線上版本增加,出現需要分平臺分版本投放的情況

  • 業務複雜性上升,二維表無法滿足業務需求

新的業務需求推動配置中心的一次大重構,開始重新梳理配置資訊的資料結構,並建設獨立應用。

新配置中心

配置中心的資料結構必須兼顧統一性個性化。統一的資料結構是抽象管理邏輯的基礎,針對統一的資料結構可以設計出通用的管理介面,儲存邏輯;而具有一定的個性化能力才能針對不同的場景更好的滿足業務需求。

模型

基於這樣的考慮,我們設計了Key-List-Object的三層資料結構。也就是說,整體上配置資料是一個Map,Map中的每一項都是一個List,而List中的項則不規定結構,可以自由發揮。Key-List-Object結構對應到業務上:

  • Key - 表示模組名

  • List - 表示模組資料

  • Object - 表示模組中的一條配置項

配置項,也就是Object是整個資料模型中的原子。

此外,為了支援跨平臺和版本的需求,還設計了應用-平臺-版本-模組的元資訊模型。

  • 應用 - 這個系統可以支撐多款應用

  • 平臺 - 如:iPhone,Android Phone,iPad,Android Pad

  • 版本 - 就是App的軟體版本

  • 模組 - 對應到資料結構的Key上

元資訊模型和資料模型結合起來組成了完整的配置中心資料模型。資料項直接關聯應用,模組和版本號三個元資訊,版本號關聯平臺,模組關聯應用。在這樣的關聯下,任意一個請求到達後:

  • 提取請求元資訊:應用,平臺,版本號

  • 根據提取的應用找到關聯的模組列表

  • 根據平臺,版本,模組獲取全部相關配置項

  • 把全部構建成Key-List-Object的結構返回

強大的橫向能力

在這樣的技術方案下,這個新配置中心可以滿足幾乎全部橫向需求,支撐多個應用,多平臺,多版本同時管理。

修改某一個配置項並在多個版本同時生效,這樣的操作非常常見,而在新技術方案下,給每一個版本的App都分配了獨立的配置項資料。為了解決這個問題,我們給所有有關的配置項設計了一個獨立的關係,當對某一個版本中的某一個配置項進行查詢,同時可以查詢到與之關聯的其他版本中的項。修改某一個配置項也可以同步到同一個關係下的其他關聯項。通過關係解決了對配置資訊的關聯處理。

全個性化的配置項

以上的模型滿足了統一性,而個性化則在配置項上體現。在Key-List-Object結構中配置項沒有硬性的格式要求,所以每一個項都可以有其個性結構,只需要滿足JSON Schema的要求即可。

這樣的設計也可以保證對List沒有需求的場景,可以在首個配置項中有最高程度的設計自由度,定義適合業務的結構。

增量更新

之前說到新配置中心還要解決配置資料尺寸膨脹的問題。而隨著業務增長,配置中心所支撐的業務模組已經超過了150個。在這樣的體量下,一份完整的配置檔案已經接近200KB。顯然,每次都載入這樣的一個檔案是不現實的,因此我們開發了針對模組級別的增量更新功能。

任意一個模組發生資料更新後,將向系統提交一次版本變更請求,整個配置版本號加1。配置中心記錄了每一次版本變更的模組資訊。當資料請求到達配置中心後,系統將比對當前最新版本和請求上行的App本地版本,並計算出版本差之間發生變更的模組資料,構造一次增量資料返回。

在全部模組中,最大的一個模組資料不超過20KB,頻繁更新的模組數量不多於10個,增量功能節約客戶端資料流量超過90%。

縱向延伸的資訊通道

至此一個功能完善的配置中心初具規模,然而隨著配置中心接入的業務的增長,一個新的課題擺在我們面前。配置中心已經託管了幾乎整個App的配置資訊,系統的橫向擴充套件只能積累量變,要進一步刺激業務能力升級就需要配置中心縱向擴充套件,在靜態配置之外的領域具備更強的功能。

縱向能力擴充套件的思路也有不同。發展配置中心繫統本身,針對需求擴充套件外延是傳統思路。然而我們認為這樣的做法不能最大限度的發揮配置中心所具備的的能力,資源產出比太低。如果可以調動既有的其他系統的,推動既有系統的無線化,發揮配置中心的通道能力,就可以調動前無線時代我們積累的大量成熟系統能力,讓這些能力迅速在無線平臺上發揮作用。這種做法不僅充分擴充套件了配置中心的縱向能力,又推動了既有系統的無線化升級轉型,事半功倍。

對配置中心的縱向改造,我們從三個方面入手。

全開放的服務介面

依據所承擔的功能,把配置中心拆分成閘道器、核心服務和介面系統三個部分,並對其進行服務化改造。每一部分都具備獨立提供服務的能力,而他們之間的配合也通過服務化的介面來實現。

如此一來,每一個部分所具備的能力都都可以提供給配置中心繫統以外的其他系統,而且拆分後的系統,每一部分的釋出頻率,系統要求,應用特點各有不同,可以針對性更強的進行系統優化。

提升資料通道穩定性

舊的配置中心設計中,閘道器直接返回核心系統計算出的配置資料,只是通過記憶體快取來提升訪問效率和穩定性。而新的設計下配置中心將為更多業務提供資料通道服務,必須選擇更安全可靠的通道方案。

我們決定把靜態配置資料按照業務切分,以橫向屬性組合為標示,投放到CDN上。通過閘道器下發CDN連結,App端SDK拿到連結後自行下載資料。從而降低核心模組的壓力,提升穩定性。

高可定製的操作介面

此前,配置中心的介面趨於同質化,誠實的還原了Key-List-Object的資料結構,而配置項的編輯介面則是一個標準的JSON Editor。為了提升可用性,我們獨立設計介面系統。

在這個介面系統中,業務方可以使用標準的JSON Schema來定義一張操作介面,也可以通過撰寫HTML模版實現個性化更強的介面。介面的表單資料將被提交到一個負責格式化和校驗的JavaScript方法中。在這個方法中表單資料被處理成系統所需要的配置資料,投放給核心服務。

不要寫死,永恆的話題

不要寫死,一個永恆的話題,這個話題會一直持續下去。而動態性這件事,是移動裝置App當下最熱門的話題。在PC時代,我們的系統經歷了C/S到B/S的轉換,終於實現了最大程度的動態化。而在無線時代,移動裝置有他獨特的屬性,B/S模式無法滿足無線時代的業務需求,至少當下是這樣。那麼Native動態化這條路,就還需要我們堅定的走下去,這條路的盡頭可能是另一個B/S模式,也可能我們找到了完美的Dynamic Wireless C/S模式。