1.背景
1.1 移動端跨平臺技術的介紹
移動端的跨平臺技術不是一個新話題,早在幾年前,WebView容器、React Native、Weex、Flutter、小程式等移動端跨平臺框架就風起雲湧。為什麼跨平臺這麼有吸引力呢?我們設想一下如果可以做到一次開發,多端複用,那麼對於公司來說,就可以降低用人成本。對於開發來說,只需要學習一個框架,就可以在Android和iOS雙平臺上開發。節約下來的成本,可以投入到產品快速驗證、快速上線。這對所有人來說都有著極大的吸引力。本節先針對部分移動端跨平臺技術進行一些簡要的介紹,以便讀者能夠更好地理解後面的內容。
1.1.1 WebView容器
WebView容器的工作原理是基於Web技術來實現介面和功能,通過將原生的介面封裝、暴露給JavaScript呼叫,JavaScript編寫的頁面可以執行在系統自帶的WebView中。這樣做的優勢是,對於前端開發者比較友好,可以很快地實現頁面跨端,同時保留呼叫原生的能力,通過搭建橋接層和原生能力打通。但這種設計,跨端的能力受限於橋接層,當呼叫之前沒有的原生能力時,就需要增加橋。另外,瀏覽器核心的渲染獨立於系統元件,無法保證原生體驗,渲染的效果會差不少。
1.1.2 React Native
2015年,Facebook推出了React Native,一經推出就備受關注。它的思路是最大化地複用前端的生態和Native的生態,和WebView容器的最大區別在於View的渲染體系。React Native拋棄了低效的瀏覽器核心渲染,轉而使用自己的DSL生成中間格式,然後對映到對應的平臺,渲染成平臺的元件。相對WebView容器,體驗會有一定的提升。不過,渲染時需要JavaScript和原生之間通訊,在有些場景可能會導致卡頓。另外就是,渲染還是在Native層,要求開發人員對Native有一定的熟悉度。
1.1.3 Flutter
2018年Google推出Flutter,通過Dart語言構建一套跨平臺的開發元件,所有元件基於Skia引擎自繪,在效能上可以和Native平臺的View相媲美。Flutter站在前人的肩膀上,參考了React的狀態管理、Web的自繪製UI、React Native的HotReload等特點,同時考慮了與Native通訊的Channel機制、自渲染、完備的開發工具鏈。Flutter與上述Recat Native、WebView容器本質上都是不同的,它沒有使用WebView、JavaScript直譯器或者系統平臺自帶的原生控制元件,而是有一套自己專屬的Widget,底層渲染使用自身的高效能C/C++ 引擎自繪。但大部分移動端發展到今天,都已經形成了自己的架構,在現有基礎上加上Flutter,會形成原有架構和Flutter雙平臺共存的問題。目前,對新的App來說,是最被看好的跨端方案。
1.2 美團外賣業務介紹
作為中國領先的生活服務電子商務平臺,美團致力於用科技連線消費者和商家,提供服務以滿足人們日常“吃”的需求,並進一步擴充套件至多種生活和旅遊服務。而作為公司最為重要的業務之一,美團外賣從2013年建立以來,已經從單一的品類擴充套件到附近美食、水果、蔬菜、超市、鮮花、蛋糕等多品類,從早午晚餐,發展到下午茶、宵夜,中餐、西餐、家常菜、小吃、快餐、海鮮、火鍋、川菜、蛋糕、烤肉、水果、飲料、甜點等多種類餐飲。美團外賣可以說是當前電商領域,最為複雜的業務之一。
業務的複雜,給系統架構也帶來了不小的挑戰。美團外賣業務之所以說是當前電商領域最為複雜的業務,主要源於以下幾點特徵:
- 美團外賣業務承載在三個App上,美團外賣App、美團App外賣頻道、點評App外賣頻道。
- 美團外賣作為美團公司重要的使用者入口,還承擔著流量平臺的作用,提供平臺能力支撐頻道業務的發展,如閃購、跑腿、金融等。
- 美團外賣作為已經成熟的業務,需提供可複用的平臺能力,支撐新業務的發展,例如菜大全App的發展。
- 美團外賣作為一個超級業務方,業務內又運營多個方向業務,如流量業務、交易業務、商家業務、商品業務、營銷業務、廣告業務等。
綜上所述,可以發現美團外賣不僅僅自身業務比較複雜,而且對外的角色也很複雜。在美團內部,外賣不僅僅是美團平臺的一個頻道業務,而且自己本身也是一個平臺業務,同時美團外賣還承擔著新業務發展的平臺角色。這意味著想要支援好美團外賣業務的發展是一件非常有挑戰的事情。
1.3 美團外賣移動端歷史架構概述
好的架構源於不停地衍變而非設計。美團外賣的架構,歷史上也是經歷了很多次迭代。由於外賣業務形態不斷地發生變化,原有的設計也需要不斷地跟隨業務形態進行演進。在不斷探索和實踐過程中,我們經歷了若干個大的架構變遷。從考慮如何高效地複用程式碼支援外賣App,逐漸地衍變成如何去解決多端程式碼複用問題,再從多端的程式碼複用到支援其他頻道業務的平臺架構上。在平臺化架構建設完成後,我們又開始嘗試利用動態化技術去支援業務快速上線的訴求。如今,我們面臨著多端複用、平臺能力、平臺支撐、單頁面多業務團隊、業務動態訴求強等多個業務場景問題。下文我們針對美團外賣移動端架構的變遷史,做一些簡單的概述,以便讀者閱讀本文時能有更好的延續性。
1.3.1 元件化架構
早期階段,美團外賣作為公司的一個孵化業務,在2013年底完成了美團外賣App的1.0版本。隨著外賣業務的驗證成功和跑通,訂單量也快速增長,在2014年底突破了日訂單量100萬。隨後在2015年2月,外賣以Native的形式接入美團App,成為美團App的一個業務頻道。在接入過程中,我們從美團外賣App拷貝了大量的程式碼到美團App的外賣頻道,兩個App上的外賣業務程式碼也分別由兩個獨立的團隊維護。早期外賣業務變化快,App迭代頻繁,寫程式碼的方式也比較粗放,同時美團App也處在一個平臺化轉變的時期,程式碼的穩定性和質量都在變化和提升當中。這些因素導致了外賣程式碼內各子系統之間耦合嚴重,邊界模糊,“你中有我,我中有你”的情況隨處可見。這對程式碼質量、功能擴充套件以及開發效率都造成很大的影響。此時,我們架構重構的目的,就是希望將各個子系統劃分為相對獨立的元件,建設元件可以直接複用,架構如下圖所示:
1.3.2 平臺化架構
如上文所述,大家可以知道美團外賣和美團外賣頻道是由不同的團隊在維護髮展。2015年,公司考慮到業務發展的一致性,將美團外賣頻道團隊正式歸於美團外賣。從組織架構上來說,美團外賣和美團外賣頻道,逐漸融合成一個團隊,但是兩端的差異性,導致我們不得不仍然階段性地維持原有的兩班人馬,各自去維護獨立外賣App和美團外賣頻道。如何解決這個問題?兩端程式碼複用看起來是唯一的途徑。另外,隨著業務的快速發展,外賣App所承載的業務模組越來越多,產品功能越來越複雜,團隊規模也越來越大,如閃購、跑腿等業務想以獨立的Native包的形態接入外賣App,還有外賣的異地研發團隊的建立,都帶來了挑戰。這使得我們在2017年開始了第二次架構重構——平臺化架構,目標是希望能夠支援多端複用和支援不同團隊的業務發展。通過抽象出平臺能力層、業務解耦、建立殼容器,最終實現了平臺化架構,架構如下圖所示:
1.3.3 RN混合架構
在平臺化架構之後,美團外賣功能持續增加,美團外賣客戶端安裝包的體積也在持續增加。回顧2017年和2018年,每年幾乎都增長100%。如果沒有一個有效的手段,安裝包將變得越發臃腫。另外,由於原生應用需要依託於應用市場進行更新,每次產品的更新,必須依賴使用者的主動更新,使得版本的迭代週期很長。業務上的這些痛點,不斷地督促我們去反思到底有沒有一種框架可以解決這些問題。
在2015年的時候,Facebook釋出了非常具有顛覆性的React Native框架,簡稱RN。從名字上看,就可以清楚的明白,這是混合式開發模式,RN使用Native來渲染,JS來編碼,從而實現了跨平臺開發、快速編譯、快速釋出、高效渲染和佈局。RN作為一種跨平臺的移動應用開發框架,它的特性非常符合我們的訴求。美團也積極的探索RN技術。在RN的基礎上,美團在腳手架、元件庫、預載入、分包構建、釋出運維等方面進行了全面的定製及優化,大幅提升RN的開發及釋出運維效率,形成了MRN(Meituan React Native)技術體系。
從2018年開始,美團外賣客戶端團隊開始嘗試使用MRN框架來解決業務上的問題。使用RN的另一方面的好處是,能逐漸的抹平Android和iOS開發技術棧帶來的問題,使用一套程式碼,兩個平臺上線,理論上人效可以提升一倍,支援的業務需求也可以提升一倍,架構如下圖所示:
2. 美團外賣容器化架構全景圖
2.1 什麼是容器化架構
上文說到,外賣業務已經發展到多App複用、單頁面多業務團隊開發的業務階段。要滿足這樣的業務場景下,尋求一個可持續發展的業務架構是件不容易的事情。經過我們之前架構演進,我們獲得了寶貴的經驗:在平臺化架構的時候,我們將App和業務進行解耦,將App做成殼容器,業務形成獨立的業務庫,整合到殼容器裡面,從而遮蔽了多App的問題,提高了業務的複用度。在RN混合式架構裡面,我們引入了RN容器,通過這個容器,使得業務遮蔽了Android和iOS的平臺差異。藉助這些成功的經驗,我們進一步思考,如果我們嘗試進一步的細分外賣的業務場景,將不同場景下的基礎能力建設成殼容器,業務整合到容器內,是否可以更好的支撐我們多App複用、單頁面多業務團隊的當前現狀呢?
容器化架構的願景是:
- 希望將前端呈現業務的環境抽象出來,將能力進行標準化,形成統一的容器,通過容器去遮蔽平臺和端的差異。容器提供上層標準統一的能力介面,使得業務開發人員專注於容器內的業務邏輯的實現,最大複用已有的能力,而不用關注現在的環境是Android還是iOS,現在的端是美團App還是大眾點評App。
- 容器和遠端達成呈現協議,使得端上的內容具備隨時可變化的能力。容器化架構的實現是存在一定前提的,如果業務的發展本身處在一個探索階段,還有較多可變的因素,是無法形成穩定的能力層的,這時候建設容器化架構反而使得架構偏向複雜。但對於外賣業務場景來說,經過多年的沉澱固定,外賣業務逐漸形成了一套穩定的業務形態,已經進入到場景細分和快速迭代業務模組的階段。在這樣的階段下,容器化架構才有可實施的前提。
2.2 容器化架構的優勢
當我們把承載外賣業務的環境進行了抽象和標準化後,就可以獲得以下若干點好處。首先動態化屬性提升,我們可以把原有必須在客戶端上寫的業務放到了遠端,業務的動態性得到很大的提升,具備隨時上線業務的可能。對於開發過程而言,編譯部署的速度也得到了極大提升。如果涉及到客戶端的程式碼改動,那客戶端的編譯打包,即使是增量的編譯,也至少是秒級的編譯速度。而容器化後,我們只打包必要的業務,把業務動態下發到容器呈現,客戶端程式碼本身不會有變化,這樣就可以從秒級的編譯減少到毫秒級的編譯。同樣,業務動態下發,對減少客戶端的包大小也有很大的幫助。
然後,容器位於應用之內,我們嚮應用中引入相同的容器SDK,容器遮蔽了應用之間的差異,對於Android和iOS平臺,在設計上,通過容器這一層去儘可能遮蔽平臺之間的差異,使業務開發人員只需要認識容器,不需要花費大量的精力去關注應用和平臺之間的差異,從而使得開發效率得到了極大的提升。
其次,容器化後,容器對承載的內容是有介面協議要求的,承載的內容只有滿足容器定義的協議才能得到容器帶來的好處,這促使業務得到了更細粒度的細分,業務開發時候,對模組化的意識得到了保障。另外,容器這一層提供的介面在Android和iOS上是標準化的,業務的開發也因為依賴的標準化,而趨向標準化,雙端的業務一致性得到了提升。這些潛在的架構好處,對未來的業務維護和擴充套件都打下了比較好的地基。
2.3 外賣容器化架構全景圖
整個外賣容器化架構可以按照從下到上,從左到右的視角進行解讀:
最底層是系統服務,因為我們採用了H5和RN這樣跨端的技術棧,使得iOS系統和Android系統成為了最底層。
系統服務之上是集團基於Native建設的基建,全公司通用,覆蓋了研發工程中方方面面的基礎服務。
在基建之上是我們定義的容器層。我們嘗試用單一技術棧解決所有問題。但經過我們的探索,覺得不太可能實現。好的架構要匹配業務形態,業務的訴求決定了我們不能選擇唯一的技術棧去解決所有問題,細分外賣的業務場景可得到以下3個方向的頁面分類:
- 高PV主流程頁面,例如首頁、金剛頁、商家頁等,這類頁面的PV遠高於其他頁面。這類頁面的特點是,互動強、曝光度高、多團隊參與建設和維護。針對這類頁面,為了給使用者提供極致的體驗,是無法採用H5或RN實現的,即使效能上能夠滿足,但是這些頁面是多團隊共同參與建設,如果用H5或RN實現,那麼單一團隊改動,都會造成全頁面受到無法預料的影響,其他未改動的業務方肯定是無法接受的。所以這類頁面,我們採用的是區域性動態化+頁面模組化的方案,我們針對頁面進行容器化改造,將頁面變成容器,容器承載模組。每個模組歸不同的業務方進行維護,從模組的維度進行解耦,每個模組都可以動態配置和下發。
- 低PV輔助頁面,例如幫助、足跡、收藏等,這類頁面的PV低,但勝在數量多,都用原生實現成本比較高,如果都用H5來實現,不少頁面和原生的關聯還是比較近,不是非常適合。針對這類頁面,我們採用集團提供的MRN基建去承載,MRN作為跨端的技術棧,我們已經在之前的RN混合架構的時候,建設了較為豐富的元件,針對自定義的MRN容器做了比較豐富的建設。同時,MRN具備動態下發的能力,滿足業務的訴求。
- 向外投放的運營活動頁面,例如聖誕節活動,時效性比較強。這類頁面由H5技術棧去完成,一方面可以滿足時效性,另一方面H5的動態下發能力也是最強的,這樣的特效能夠充分的滿足業務的訴求。我們使用集團提供的Titans基建,通過建設業務自定義的Titans容器,支撐業務的發展。
再往上,就是垂直的業務,外賣目前有流量業務、交易業務、商家業務、商品業務、廣告業務、營銷業務、閃購業務等。業務都是垂直向下依賴,直接可見容器,可見基建,能夠很好地獲取到各種已經建設的能力去完成業務的需求。
最上面是承載的App端,目前有四端,包括外賣、點評、美團、閃購等等。
右側是測試釋出和線上監控,相對於常規的移動端App架構而言,容器化架構的測試釋出和監控是更為精細化的。不僅僅要關注端本身的可用性,還需要關注容器、容器承載的模組、模組展示的模板,模板裡面的樣式這些的可用性。
2.4 容器化的挑戰
容器化架構相對常規的移動端架構而言,它從管理移動端的程式碼轉變成管理移動端的容器建設程式碼和業務遠端開發程式碼,多出了容器和業務遠端下發。這不僅僅是對技術上的挑戰,對長期做客戶端開發同學,也需要一個思維轉變的跳轉。
一致性的挑戰:容器需要在多個宿主應用之中執行,宿主應用的環境一致性直接影響了容器的一致性。我們的策略是兩手準備,一方面利用外賣業務的優勢推動宿主應用的環境對齊;另一方面將容器建設成SDK,通過SDK將長期保持容器的一致性,也通過SDK內部的設計遮蔽應用之間的差異;對於Android和iOS平臺,我們通過分層的設計,儘可能遮蔽平臺的差異。綜上所述,一致性的挑戰在於(1)容器執行的宿主應用的環境一致性;(2)不同應用不同版本容器的一致性;(3)Android和iOS平臺容器的對業務的一致性。
動態釋出的挑戰:長期以來,客戶端同學的開發概念裡面只有App版本的概念,而當我們逐漸把業務程式碼做成遠端下發時,將會新增一個線上動態發版的概念。當我們在釋出業務的時候,相對以往的工作,多出需要去考慮這個業務的版本,可以執行的容器對應的App上下界版本。另外,發版的週期也會新增業務的發版週期,不僅僅是App的發版週期。這兩者在一起將會產生新的火花,業務的版本和App的版本如何適配的問題,業務動態發版的週期和App的發版週期如何適配的問題。外賣這邊的解決方式是建設主版本迭代+周迭代的模型。
監控運維的挑戰:以往的移動端架構,我們更加關注的是端本身的可用性,然而當我們演進到容器化架構的時候,僅僅關注端的可用性已經遠遠不能確定業務是可用的了。我們需要從端的可用性延伸出下載鏈路、載入鏈路,使用鏈路上的可用性,針對每個重要的環境,都做好監控運維。
3. 外賣跨端容器建設
3.1 MRN容器
3.1.1 MRN容器簡介
React Native框架本身只是一個執行時環境中的渲染引擎,可以將同一套JS程式碼分別在Android和iOS系統上最終以Native的方式渲染頁面,從而為App提供了基礎的跨端能力。但從工程化的角度來看,如果想在App中大規模地應用RN技術,除了RN框架本身外,還需要在開發、構建、測試、部署、運維等諸多方面的配合。MRN(Meituan React Native)是美團基於React Native框架改造並完善而成的一套動態化方案,在RN的基礎上提供了容器化能力、動態化能力、多端複用能力和工程化保障。MRN在開發效率、穩定性、效能體驗、動態化和監控運維等多方面進行了能力升級和擴充套件,滿足了美團RN開發工程化的需要。目前,MRN已接入美團40多個App,核心框架及生態工具有超過100位內部程式碼貢獻者,總PV超過4億。
3.1.2 Roo元件庫
下面再介紹一下外賣建設的兩個UI相關的技術專案,Roo元件庫和元件樣式動態配置。
- Roo元件庫:外賣在RN及MRN框架提供的UI元件基礎之上,又擴充套件了適用於外賣業務的標準化UI元件庫。UI元件庫一方面統一了我們的設計規範和開發規範,提高了UI一致性;另一方面,元件封裝也提升了RN頁面的開發效率和質量。
- 元件樣式動態配置:有了標準的Roo元件,我們進一步給標準元件的動態新增了樣式動態配置能力。在使用元件時,很多樣式是支援動態下發的,例如字型、圓角、背景色等,方便我們進行UI的適配和改版。
外賣在2018年底開始試驗MRN容器在外賣業務上的應用,並在2019年上半年進行了大面積的頁面落地。目前,外賣已有近60個RN頁面上線,佔外賣頁面比例超80%,其中包括Tab頁面“我的”、提單選擇紅包頁、訂單評價頁等高PV頁面。MRN容器的接入,給外賣App的容器化、動態化、人效提升、包大小瘦身等方面都做出了不小的貢獻。
3.2 Titans容器
3.2.1 Titans容器簡介
Titans容器是美團系App統一的Web容器元件,基於蘋果提供的WebView元件,將WebView容器化,統一了WebView的UI展示和互動方式,規範了橋協議的使用正規化,同時預置了諸多基礎能力和業務能力。Titans容器大大提高了Web頁面的開發效率和使用者體驗上的一致性。
- Web容器:Titans容器提供了統一的UI展示和自定義樣式,例如導航欄樣式、頁面Loading狀態、進度條樣式等;還有統一的互動方式,例如頁面跳轉、Scheme協議的解析等。
- KNB統一橋服務:Web容器雖然在動態化和Android、iOS雙端複用上很好地彌補了Native的不足,但在很多地方體驗上又難以達到Native的標準。因此,KNB橋應運而生,KNB定義了Native和JS通訊的標準方式,方便開發時進行橋協議擴充套件,同時KNB也內建眾多的Native基礎能力,極大地提高了Titans容器的使用者體驗和開發效率。
- Web業務增強能力:Titans容器中預置了豐富的基礎業務頁面,例如登入頁面、分享彈窗等。
- 提供鏈路增強:提供了長連線、離線化等方式來提高網路請求的速度和成功率。
- WebView預載入:在App啟動之後,使用者點選網頁入口之前,提前載入好HTML主文件和基礎庫,這樣當使用者點選頁面入口時,App直接使用已準備好的WebView,僅需載入少量的業務程式碼。從而達到白屏時間短、載入頁面迅速的效果。
Titans容器在外賣業務中的使用場景非常豐富,其中最重要的使用場景是各種運營頁和活動頁,例如點選首頁頂部Banner的廣告落地頁、為你優選、限時秒殺等活動運營頁等;還有客服頁、幫助反饋頁、商家入駐頁、美團公益頁等功能性頁面;作為一級入口頁面的美團會員頁面,也是一個基於Enlight的Titans容器。
4. 外賣頁面容器建設
外賣容器化建設,首先需要要區分的是核心頁面和非核心頁面。外賣業務中對核心頁面的定義是頁面DAU>美團DAU的5%或者是下單關鍵路徑。為什麼要先按照是否為核心頁面進行拆分呢?重點就在於改造的成本。核心頁面的業務複雜度決定了它不容易做全頁面的動態化,它比較適合做區域性的動態化方案。核心頁面的複雜度在於業務本身複雜,最重要的是核心頁面往往會有多個垂直業務團隊共同的開發維護,大家各自有重點關注的模組,做全頁面的動態化,無法做到有效的物理隔離。
而對於非核心頁面,業務功能和互動相對簡單,組織關係也較為確定,更適合做標準的MRN和Titans容器化。所以我們的策略是核心頁面做到支撐頁面模組級別的業務動態和複用,非核心頁面可以做到頁面級別的動態化和複用。頁面容器化的核心含義就是把一個頁面劃分為若干個模組,每個模組成為一個業務容器,每個容器的填充既可以用Native的方式實現,也可以用Mach實現(Mach是外賣自研的頁面區域性動態化技術),可以支援iOS/Android/小程式三端跨平臺執行。頁面本身則化身為容器的管理者,負責子容器的編排和佈局,並支援其動態化。
4.1 頁面容器化設計思路
頁面容器化設計中主要分為三個階段,模組有序化、模組編排化、漸進式業務落地。
- 模組有序化:將耦合的外賣業務程式碼按模組維度進行拆分,建立標準化的模組間組合和互動方案,降低模組內改動對其他模組的影響。這個階段我們同時完成了Native原生模組和區域性動態化模組的標準化改造。
- 模組編排化:頁面容器化的一個特點是頁面具備編排模組的能力,在這個階段我們在客戶端增加了對業務模組結構編排能力的支援,同時我們跟後端的同學共建了配置平臺,通過配置化的方式打通了AB實驗平臺、統一資料服務等多個平臺。在標準化的資料協議的支撐下,頁面支援了AB實驗動態上線,大大降低了客戶端在AB實驗方面的開發成本,做到了客戶端零成本,後端低成本,高效地支撐了外賣首頁六週年的大改版。
- 漸進式業務落地:頁面容器化後最明顯的收益是支援業務需求的動態上線,只有頁面容器中的業務模組具備動態能力才能實現這個目標,這會涉及Native模組遷移為Mach模組的過程。在這個方面,我們的思路是跟隨業務需求漸進式落地。在模組編排階段,我們設計了Native模組向Mach模組遷移的能力,同時設計了覆蓋模板維度和API介面維度的三重降級方案,來保障模組動態化遷移的穩定性。
4.2 業務構建模組標準化
從App頁面開發的角度看,一個完整的頁面可以按照不同的功能及不同業務屬性劃分出多個不同的模組。
業務構建泛指由多個業務模組組合拼裝為一個業務頁面的過程,涉及頁面本身(UIViewController/Activity)以及各個業務模組的構造過程,前後端業務資料以及頁面和業務模組之間的資料互動過程,業務模組內部的資料處理以及檢視重新整理流程。
模組標準化指的將業務構建涉及到的多個過程通過規範化的方式確定下來,形成唯一的標準。模組標準化一方面能夠在解決業務共性問題的基礎上提供業務難點專項解決方案,另一方面能夠在框架基礎上形成能力約束,減少重複建設、低質量建設的問題。
業務構建模組標準化中我們抽象了四層,下面將分別進行解讀。
- 最底層是框架能力層,是外賣業務團隊自研的符合外賣業務特點的雙端模組化框架。框架解決了不同頁面場景下的共性問題,對典型的業務痛點也進行了支援。它是一種頁面框架設計在iOS、Android雙端對齊的實現方案,這種雙端對齊的能力為頁面容器化設計的雙端一致性提供了保障。
- 統一介面層是對框架能力層的標準化抽象,它可以保證任一模組呼叫的能力在各個業務場景下的實現都是一致的,有了這一層抽象任一模組都可以直接在各個場景下複用。
- 在往上就是App全域性的模組複用層,標準化後的模組可以通過唯一標示向模組複用池註冊模組,這種中心化的註冊方式可以讓業務模組在跨業務庫的場景下可以靈活地複用。
- 最上層就是外賣的核心業務場景層,每一個場景都對應了一個標準化的頁面容器,頁面容器通過實現容器化介面來完成頁面容器的構建。
通過業務構建模組標準化的建設,業務模組已經是標準化的了,可以在跨頁面間自由組合,這為頁面容器化打下了基礎。
在頁面容器化中最基礎的能力有以下幾點:頁面中業務模組可編排能力,動態上線前端AB實驗的能力,增量上線動態模組的能力。實現這些能力最重要的就是進行前後端資料協議標準化建設。客戶端根據資料協議中的模組唯一標識匹配並構造業務模組,在完成模組資料的填充後會根據資料協議中的模組佈局資訊完成模組的佈局。針對Mach動態模組,我們建立了基於模板ID的模組匹配和資料填充流程,可以支援Mach動態模板的增量上線。在資料協議中針對前端AB實驗我們預留了AB實驗和通參欄位,在資料填充階段通過容器化介面傳入動態模組中,用於支援AB實驗的動態上線。
在容器化上線的過程中屬於介面的大版本升級,為了保證容器的高可用性,客戶端從模組級別和API級別實現了兩套降級容災方案。
模組級別的降級方案主要針對Mach動態模組,與Native模組不同,Mach動態模組需要預先下載動態模板才能正常地完成模組的載入和渲染。為了保證動態模組的載入成功率,我們一方面在介面上線前利用Eva(美團內部系統)對Mach模板的下載進行預熱。另一方面,我們設計了動態模組的主動降級方案,針對動態模組的動態上線使用Native模組進行兜底降級,對於跟版動態模組使用App內建模板的方案進行兜底降級。
API級別的容災方案主要為了保障客戶端在新介面不穩定的情況下可以自行降級到舊介面。針對這個問題,我們對線上老介面設計了資料結構對映方案,在客戶端通過配置化的方式可以把老介面的資料結構對映為新介面的資料結構。這樣在上層業務無感知的情況下,可以做到容災方案的上下線。
4.3 小結
通過頁面容器化,使得頁面只需要關心頁面級的構造方式,而無需關心某一模組內部如何實現動態化的。把頁面與頁面的模組分離,也符合目前外賣客戶端的組織結構,有利於業務組間的協作。同時,頁面容器化使得外賣核心頁面具備了符合外賣業務場景下的動態能力,漸進式把Native靜態模組過渡到具備動態能力的模組,從模組的維度使整個頁面具備了動態能力。這種漸進式的遷移方案把容器遷移跟業務模組的遷移分隔開,大大降低了頁面容器化改造的風險。
5. 外賣容器化架構的衡量指標
5.1 容器化架構衡量指標的特點
質量和效能指標是衡量我們App開發質量和使用者體驗的重要依據,是我們一直都非常關注的重點資料。在非容器化時代,我們大多數的指標都和App的使用環節緊密相關,因為在非容器化時代,邏輯鏈路相對簡單,例如我們開啟一個新頁面時,我們首先建立頁面例項,然後發起網路請求,同時頁面會經歷一系列生命週期方法,最後渲染。這時我們可能會關注網路請求的成功率和請求時間,頁面的渲染時間,和過成功是否發生Crash,這個過程相對更短暫,指標更少,所以監控起來也更容易。
外賣的容器化大大提升了外賣業務的複用能力、動態能力、模組化和開發效率,但同時也帶來了更長的邏輯鏈路,鏈路從時間維度上劃分是:下載鏈路、載入鏈路、使用鏈路。例如我們在使用MRN容器的時候,會涉及到bundle的啟動下載或預熱下載,bundle解壓縮,MRN容器引擎初始化,bundle載入,JS的載入、執行,頁面渲染等步驟,其中的每個步驟都可能存在效能問題和質量風險。因此,我們需要升級我們的衡量指標系統來應對容器化帶來的新的挑戰。
5.2 鏈路指標
- 下載鏈路:在下載鏈路中下載容器所需的各種資源,在MRN和Mach中主要是bundle的下載任務,只有bundle下載成功,才能進行後面的各項操作。所以bundle下載的成功率是下載鏈路中最重要的指標,同時bundle下載的時機也很重要。外賣業務中有各種bundel上百個,如果在啟動時拉取所有bundle,對冷啟動時間會造成極大的影響。我們採用了bundle預熱的方法,釋出bundle是給bundle打上相應的Tag,在適當的時機去下載,避免集中下載。
- 載入鏈路:在載入鏈路中重要工作是對下載鏈路中下載的資源的解壓和解析。例如在用PGA載入頁面時,會進行模組的解析、模組匹配、模組降級、資料模型生成等步驟。在MRN中會進行bundle解壓、引擎初始化、bundle載入等步驟。載入鏈路往往是比較消耗計算資源的步驟,對頁面開啟和載入時間影響較大,所以我們會比較關注載入鏈路的效能指標。
- 使用鏈路:使用鏈路和非容器化的使用階段基本相同,會主要關注頁面的載入時間、Crash率、頁面頁面FPS、頁面卡頓等指標。除此之外,還會關注和容器本身特性相關的一些指標,例如在MRN容器中,我們還會關注JS錯誤率、JS渲染時間、白屏率等指標。
5.3 關鍵指標
因為容器化的使用形成了一個序列的鏈路,所以如果某個關鍵節點失敗,會導致容器功能不可使用,關鍵指標的任務就是從上述眾多的指標當中篩選出這些關鍵節點。例如在下載鏈路中bundle下載的成功率和API的成功率,載入鏈路中bundle載入的成功率和模組匹配的成功率,下載或載入失敗都無法再進行鏈路中的後續步驟,針對上面的成功率指標,我們會新增分鐘級別的實時監控告警,做到及時發現,快速響應和緊急修復。
在使用鏈路中模組渲染的成功率、Native Crash率、JS錯誤率也屬於關鍵指標,這些任務的失敗也會導致容器的不可用,針對這些指標我們也會採用實時監控措施,並且新增降級手段,例如回滾bundle版本,或者把MRN和Mach容器降級為Native容器。
6. 外賣容器化架構的監控運維
上面講到了容器化架構的各項衡量指標,那麼把這些指標具體落到實處的工作就是線上的運維監控工作。工欲善其事,必先利其器,對於監控運維工作,一定要有合適的監控工具輔助配合才能事半功倍,公司內有很多優秀的監控統計工具可供使用,這裡的難點就是如何根據監控的需要判斷選擇合適的工具。還有就是合理的劃分監控維度和資料指標的優先順序,例如對於能夠影響到鏈路穩定性的關鍵指標,我們需要做到分鐘級的監控,一旦出現問題就能及時收到告警,對於非關鍵指標,則通過生成日報的方式,方便開發者的統計和分析。
工具的使用上主要分為大盤工具、具體異常工具、灰度降級工具、告警工具等(以下是美團內部使用的工具)。
- 大盤工具:主要使用CAT、天網、Crash平臺等工具。這些工具收集、統計大盤資料,然後生成視覺化的圖示和曲線。方便開發者瞭解大盤的整體情況和變化趨勢。
- 具體異常工具:使用Sniffer、Logan等工具。這些工具可以用來獲取發生異常時的上下文和裝置資訊,回撈使用者行為日誌,方便定位排查具體問題。
- 灰度降級工具:使用ABTest平臺、Horn等工具。用於下發配置,以進行灰度控制或開關控制。
- 告警工具:告警小助手。將告警通過IM軟體及時傳送到個人或群組,做到及時發現及時處理。
業務覆蓋維度監控可以分為全域性監控和區域性(單業務)監控。
- 全域性監控:監控各項容器化質量指標、效能指標,生成每日報表,方便跟蹤監控容器化的整體質量。
- 區域性(單業務)監控:實時監控每個頁面、每個容器的線上資料,做到有問題及時發現,及時定位,及時處理。
時間維度監控:可以按天、小時、分鐘的時間維度。天級別的監控主要是一些非關鍵路徑指標,例如一些效能指標,頁面載入時間、頁面FPS、JS渲染時間等,我們可以按天維度的生成資料包表,已郵件的資料傳送日報。當App灰度上線時,我們會開始小時級別的監控,每過半小時通過IM軟體向廣播一些關鍵指標,方便開發者跟蹤線上資料的穩定性。分鐘級別的監控則是針對關鍵指標,觀察分鐘維度上的變化,如果關鍵指標超過閾值,或者波動過大,就會及時產生告警。
下面我們以一個開發者的視角去看一下外賣容器化架構的監控運維繫統。從獲取資訊的方式上可以分為主動查詢和被動推送,開發者可以通過監控工具監控全域性和區域性資料的變化趨勢,也可以分析具體異常Case;也可以從IM工具,郵件等收到相關的推送資料,以便及時響應。在控制運維上,開發者可以通過Eva、Horn等美團內部的灰度系統進行灰度釋出,當灰度期發現問題的時候,可以及時地通過停止灰度,版本回滾,關閉入口的方式進行降級容災處理。
7. 外賣容器化架構的釋出能力
7.1 容器化架構釋出體系
容器化使外賣業務具備了強大的動態化能力,但動態化能力又和需要相應的釋出能力來支援,釋出能力是我們業務開發質量和效率的重要保障,也是我們容器化建設工作過程中的重點環節,這一節主要介紹一下外賣容器化的釋出能力。
從釋出能力型別的角度看主要可以分為三種型別:(1)容器內容的釋出,包括髮布整個頁面或者釋出頁面中的區域性模組;(2)配置下發,通過API或其他配置平臺,下釋出局協議、AB測試、樣式配置、功能配置、模板配置、容器配置等,大大提高了業務的靈活度和線上驗證能力;(3)灰度、降級下發,通過UUID,使用者畫像等資訊做到灰度釋出,降級回滾等控制能力。
從釋出資源的的角度看主要分為兩種:一種是普通的資源,例如釋出一個Web頁面,或者通過釋出新版API來控制頁面區域性容器的展示與否和展示的位置,同時我們也可以進行一些AB Test操作;另一種是bundle資源,主要是針對MRN容器和Mach容器,每個MRN容器和Mach容器的資源都會先被打包成一個bundle,然後通過釋出系統下發到終端,然後終端解析bundle中的程式碼和資源,最終渲染頁面。
從釋出階段的角度看,可以分為測試階段、上線階段、灰度階段和全量階段,其中上線階段是最終的環節,我們增加了很多校驗和保護手段來儘量保證上線操作的正確性。
7.2 跟版本釋出流程
雖然我們具隨時備動態釋出能力,但正常的版本迭代還是會存在中,所以外賣這邊的節奏是周動態迭代+雙週版本迭代,這保證了我們的開發工作有個一清晰的週期。在動態釋出階段中最關鍵的階段操作上線階段。以MRN為例,目前外賣業務中應有70多個bundle,再算上測試環境的bundle就有接近150個bundle,只是管理這些bundle就是一個複雜的工作,況且在進行上線操作時還是涉及釋出的目標App、App版本的上下界、MRN版本的上下界等,一不小心就會造成操作失誤,所以進行上線操作時需要非常謹慎。
我們針對操作上線階段進行了事務流水線,通過流水線建立保護措施,一個bundle的上線要經歷一個流水線的若干操作。首先,操作人根據上線SOP手冊進行若干檢查操作,同時編寫標準格式的釋出說明,然後周知相關核心人員後在作業系統上發起上線申請,Leader和QA收到申請後會進行檢查並審批,審批通過後還要避開App使用的高峰期或節假日上線,上線後通過灰度釋出觀察各項資料指標,指標正常後全量釋出。
7.3 bundle資源釋出
bundle是我們最常釋出的資源型別,這裡再結合釋出工具講解一下bundle的釋出過程。MRN和Mach都是以bundle的形式下發到裝置終端的,我們在釋出bundle的時候主要會用到兩個工具,打包工具Talos和釋出工具Eva(美團內部工具)。一個bundle的工程檔案主要由三個部分組成:配置檔案、原始碼和資原始檔,其中配置檔案用於指導Talos對工程檔案進行打包,多個bundle可以共享一份配置檔案。當我們準備釋出一個bundle時,先找到該bundle在Talos的釋出模板,選擇釋出環境(測試或線上),然後進行一鍵打包,然後Talos會進行一系列流水線操作,包括Clone程式碼、配置環境、進行Lint檢查、構建和上傳等。Talos打包完畢後將bundle上傳到Eva系統,然後Eva負責bundle的分包、上線、下線、灰度等操作,最終下發到終端裝置上。
未來,我們還將引入美團住宿的MRN-DevOps來進一步的遮蔽當前多系統的問題,降低整個週期管理的成本,特別是釋出前的人工檢查成本,逐漸實現RD在一個平臺上操作從研發到釋出運維的所有實現。儘可能地減少人工成本,提升自動化。
7.4 多種釋出能力綜合使用
上面介紹的是以bundle資源形式的釋出流程,過程較為清晰簡單。下面再結合外賣首頁,介紹一下區域性容器化的釋出方式。外賣首頁是典型的流式列表,在區域性容器化的架構下,首頁就是由一個個矩形容器以ListView方式佈局的,容器分兩種,Native容器和Mach容器,Mach容器是一個通用容器,我們可以編寫不同的樣式模板,下發到終端後交由通用Mach容器來渲染,以此達到只使用通用容器展示不同UI樣式的目的,這裡涉及了Mach的釋出系統。
首頁各子容器相當於一塊塊積木,它們的位置排布、展示與否、模板的選擇等最終交由API控制,API具備了控制首頁佈局,樣式展示的能力,而不再是單純的資料來源。同時,首頁也涉及了AB能力、灰度降級策略等其實配置項下發系統。可以看到外賣首頁的容器化是由多種釋出能力配合支撐的,是外賣釋出能力體系的“集大成者”。
8. 總結
好的架構是要隨著業務的發展,不斷演變去適應業務的發展。美團外賣從一個很小規模,每日單量只有幾千的業務,逐漸地走到今天,每日單量峰值超過4000萬,組織架構也從一個十幾個人的團隊,逐漸發展到現在多角色、多垂直業務方向,上千人共同協作的團隊。移動端上的架構,為了適應業務的發展要求,也經歷了元件化、平臺化、RN混合化,再到現在向容器化的變遷。
容器化架構相對於傳統的移動端架構而言,充分地利用了現在的跨端技術,將動態化的能力最大化的賦予業務。通過動態化,帶來業務迭代週期縮短、編譯的加速、開發效率的提升等好處。同時,也解決了我們面臨著的多端複用、平臺能力、平臺支撐、單頁面多業務團隊、業務動態訴求強等業務問題。
當然,容器化架構帶來好處的同時,對線上的可用性、容器的可用性、支撐業務的線上釋出上提出了更加嚴格的要求。我們通過監控下載、載入、使用鏈路上的可用性,來保障線上動態業務的可用性。針對容器,我們利用成熟的測試基建,建設容器的自動化測試來保障容器的可用性。針對釋出,我們建設迭代流程,配合釋出流水線,將線上的釋出變得更為可控。
截止到目前為止,外賣業務經過了幾十個動態化業務上線視窗,累積共發版百次以上。未來半年,我們還將進一步從業務需求入手,將業務需求細分歸類,讓產品側逐漸建立容器和動態化需求的概念,能夠從源頭上,逐漸的將業務進行劃分,最終使得每個業務需求,都可以歸類抽象成可以動態下發的業務和容器能力建設,從而進一步的完善容器化架構的能力和支援更多的的業務場景。
9. 參考資料
- React Native在美團外賣客戶端的實踐
- 美團外賣iOS多端複用的推動、支撐與思考
- 美團外賣前端容器化演進實踐
- UC瀏覽器客戶端容器化架構演進
- 你知道支付寶容器化架構是怎麼搭建的嗎?
- 講一講移動端跨平臺技術的演進之路
- 盤點主流移動端跨平臺UI技術:實現原理、技術優劣、橫向對比等
10.作者簡介
- 郭賽,同同,徐巨集,均為美團外賣iOS工程師。
11. 招聘資訊
美團外賣長期招聘Android、iOS、FE高階/資深工程師和技術專家,歡迎有興趣的同學投遞簡歷到wangxiaofei03@meituan.com。
想閱讀更多技術文章,請關注美團技術團隊(meituantech)官方微信公眾號。