Atlas-手淘組件化框架的前世此生和將來的路

今天手淘技術團隊宣佈正式開源它們的容器框架Atlas,項目地址:前端

https://github.com/alibaba/atlasgit

同時他們還推出了項目官網,上線了技術文檔:github

http://atlas.taobao.org/app

下面讓Atlas團隊來介紹下該項目的歷史、原理和將來。框架

Atlas是什麼運維

Atlas是古希臘神話中的天神,是波士頓動力公司的機器人,藉助搜索引擎,得以發現這個名詞背後許許多多的含義。在手機淘寶,Atlas是一個紮根於Android客戶端的一個組件化容器框架,相比神話中用手和頭支撐起蒼天的泰坦神族,Atlas在手淘默默無聞地承載着手淘上豐富業務的運行,伴隨着數不清的功能在用戶手中經歷新老交替。異步

插件化的歷史函數

在阿里,在手機淘寶,移動客戶端的迭代更新能夠說是整個移動互聯網重新起到繁榮的見證。伴隨着愈來愈多業務的誕生,愈來愈多的代碼開始往小小的客戶端涌入,而不管是iOS仍是Android,各類客戶端的體積也經歷着快速的增加。而因爲客戶端的推廣成本較高,各個業務線都有很強的需求接入手機淘寶客戶端。因此在當時航母戰略的背景下,迫切須要一種技術能把客戶端化整爲零,這些模塊能夠自由組合,而且當部分功能變動時只須要更新對應模塊。組件化

咱們在2012年末的時候就開始研究Android上的插件化技術,並與2013年初把Atlas做爲插件化框架接入手機淘寶。基於插件化的能力,一個普通的Android應用能夠低成本地轉爲符合Atlas規範的插件,使Apk既能夠以插件方式運行也能夠獨立安裝運行。一個大的Android客戶端項目能夠分割成數個插件,作到代碼隔離,下降了開發、維護、部署的成本。性能

當時插件化的方案可分爲三類:Web、WebApp、Native。各類方案在手淘內都在有同窗進行深刻的研究。

Native形式的插件因爲技術門檻較高,當時業界採用的並很少見。可是因爲其比WebApp有良好的性能和體驗,同時對於許多已經成型的小客戶端推廣的迫切需求,尤爲值得咱們去探索。

上圖是當時插件化的技術原理的簡圖,其實跟如今業界的插件化方案主要的設計思路基本一致。Atlas做爲一個模擬的Android運行環境,每一個插件以一個獨立的進程運行在各自的沙箱環境中。同時因爲接入成本較低,其餘的業務應用基本能夠算是零成本的方式接入到手淘環境,同時也能夠隨時發佈新的版本進行更新。

插件化的方案有其特有的優點,獨立的進程保證了業務的絕對隔離,同時也便於隔離風險。聚划算、天貓、彩票一個個獨立應用開始起降於手淘這架航母。甜蜜的成長老是短暫,插件化發展很快,隨着手淘All In方案的深刻,一些隱患開始展示。

性能:新進程的開闢極大影響每一個業務的進入速度,同時因爲手淘的插件並非用完即走的場景,好比說從首頁->聚划算->詳情->店鋪->下單各個環節是存在着必定的關聯,各個插件的進程沒法退出。而多進程的機制又極大的佔用了內存,同時也引發許多用戶的質疑

複用:插件的獨立限制了許多中間件的複用,AIDL的方式並不適合中間件能力的輸出,獨立的apk直接進入的同時也攜帶了大量重複的二方庫,也一方面極大增大的應用的體積

穩定:插件化須要一個模擬的Android運行環境,在Android多版本以及國產ROM的兼容中須要作大量的工做,同時多插件運行過程當中在低端設備上很容易遇到進程被回收或者三方應用強殺的狀況,沒有良好的恢復機制會極大下降用戶的體驗

組件化的誕生

伴隨着All In的進行,在手淘內部發起了對大型app進行重構的計劃。咱們須要這樣一個框架:

支持大量豐富業務的接入,同時業務之間可以保持清晰的邊界,各自能夠繼續靈活迭代;

用一批統一的中間件去支撐起各類業務的底層功能,保持中間件代碼的全面複用;

可以儘可能保持對系統的低侵入,尊重原生運行機制以下降後期的維護成本;

在用戶設備上儘可能體現一個簡單客戶端的特性,同時特定的業務功能按需獲取,保持體積的可控;

↑新容器化結構設想↑

基於對插件化框架對Android運行機制的理解,參考了OSGI在服務端框架,在開發IDE等領域"高複用、低耦合、可插拔"的優點,咱們借鑑了OSGI規範開發了基於組件化的Atlas容器化框架。

下圖是基於組件化框架的系統結構

最底層的tookit verifier全面羅列了上層須要反射使用的注入和代理的Api,會在應用啓動時先進性全局性的校驗,以免程序運行中遇到不兼容的狀況;

往上Bundle Framework負責組件的安裝 更新 操做以及管理全部組件的生命週期,這裏組件的邊界隔離就遵循了OSGI的規範,每一個組件分配獨立的classloader,同時組件有各自的資源,每一個資源在構建期間由AAPT分配獨立的package ID;

Runtime層 主要包括清單管理、版本管理、以及系統代理三大塊:

版本:每一個組件在構建期間就由構建插件分配本身的版本號,同時安裝期間也有各自的版本目錄,每一個bundle的啓動加載都須要通過版本的校驗,組件發生更新同時也下發最新的版本信息。依託版本管理機制組件的熱更新能力水到渠成。

清單:OSGI規範中每一個組件一般經過OSGI.MF來暴露自身的component,這是與Atlas容器所不一樣的地方。在Android設備上,多文件的形式很容易受IO異常的影響干擾bundle正常運行,因此咱們採用了構建期間集中生成清單的方式,清單裏面記錄bundle全部的component(Android四大組件),依賴、packagename等內容。

代理:各個系統關鍵點的注入使得bundle能夠作到按需加載,避免了像原生MultiDex方案因爲首次啓動時多dex同步安裝而形成UI卡頓的狀況,代理層的核心DelegateClassLoader負責類的查找和路由,DelegateResource管理全部bundle的資源,它們在容器啓動時進行注入,並在運行過程當中隨着bundle的不斷載入進行更新。

接入層 簡單是美-複雜的東西留給本身。爲了方便,Atlas容器有自身獨立的Application負責啓動,同時在構建期間會由插件替換應用原有的Application。運行期間應用首先由AtlasBridgeApplication負責啓動,並在容器啓動完畢後全權代理給應用真正的Application;同時對須要自定義和由外部決策的功能,容器開放接口由接入方簡單設置。

解耦和依賴

依託classloader,組件的獨立性有了充分的保證,同時因爲BundleClassloader保持了對PatchClassLoader的引用,使得宿主的中間件就如同普通APK開發同樣能夠被組件簡單調用;同時組件之間除了基於AIDL的服務調用,組件之間提供了靜態依賴和運行時依賴,相互依賴的組件能夠經過配置打成,這在必定程度上使某些偏功能的代碼或者UI的重用成爲可能,同時配置的方式使組件之間的關係能夠清晰追溯。

性能的演進

異步按需:因爲每一個組件是一個小型Apk的結構,每一個組件安裝使其涉及到文件的拷貝、native lib的解壓以及校驗載入等過程。特別是Service等後臺component觸發組件安裝和前臺Activity引起組件安裝並行時UI的流暢很受影響。爲了下降對UI的影響,每一個組件的安裝都在一個統一的異步安裝線程中進行。Activity、Service、Receiver等的發起都被進行了異步的處理。

解釋執行組件的代碼在安裝後還須要dexopt才能使用,這一過程在ART上的時間消耗尤其明顯,爲了下降用戶等待時間,Atlas框架對dalivk系統上首次使用bundle時關閉了verify,在ART系統上首次使用時關閉了dex2oat走解釋執行,保障bundle首次進入儘量地高效,同時後臺經過異步任務走原生的dexopt過程,爲下次使用作好準備。

動態性的加強

遠程組件:插件化時代的按需下載並無被廢棄,獨立的遠程組件能夠知足用戶的個性化需求。基於用戶的操做和清單機制遠程組件在用戶須要時只須要簡單地等待或者受權就能夠從遠端拉倒本地。同時當用戶設備由於空間緊張時容器也能夠清理掉一些長期不用的組件以釋放擁擠的空間

動態部署:動態部署是容器一個最重要的功能。基於此,業務能夠靈活發佈本身的需求;有故障的業務能夠及時修復或者回滾;同時動態部署的快速覆蓋能力在灰度等場景下能夠更快地收到所需的效果。動態部署的範圍也經過改造進行擴大,從最初的組件部署到如今支持除Atlas容器小部分核心代碼外的全部代碼,部署patch包的體積和性能的改進也在不斷的進行改進。

diff

每次大版本的發版,集成平臺會保留該次發版的AP(Ap中記錄全部參與構建的二方庫的版本,組件的依賴,混淆的mapping等各類信息),在手淘接下來每週一次的動態部署迭代中,動態部署的構建會與以前的dex進行字節碼級別的diff,生成tpatch包,最終下發到用戶手機的patch僅包含變化class組成的dex和更改或者新增的資源文件

Bundle的Merge

patch收到後,客戶端的merge過程會根據patch信息,取到source 組件,source dex和patch dex進行dex合併,合成新的dex,同時變化的資源覆蓋老的資源最終造成新的組件。

中間件部署

動態部署能力的加強少不了對底層中間件的支持,部署的機制也與bundle大相徑庭

1. 中間件代碼部署

Dalvik和Art在merge過程當中都會造成新patch zip,zip中dex以多dex的方式命名,patch dex爲classes.dex,原始apk中的classes.dex以此根據序號+1放入zip中,不一樣的是dalvik因爲preVerify的限制寫入zip中的源dex會先通過處理,剔除在patchdex中已有的class,確保被patch的dex沒法被打上preVerify的標籤。

安裝過程當中則多個dex依次dexopt,同時插入到dexpatch的第一位。而ART因爲無preVerify校驗,可是Android到ART後原有的dexopt會改成dex2oat.

運行時代碼由原來的解釋執行改成直接運行native代碼,以前的使用過程當中發現單純得往前面追加patch的dex並不能徹底解決動態部署的問題,dex2oat的過程優化了class的執行代碼,好比說內斂,虛函數的校驗等。就有可能在運行過程當中直接在native層執行老的優化過的class代碼而不是重新patch的dex中load新的class去使用。

因此在ART上相似Dalivk,放入源dex,只是源dex不會作任何處理,而後合併在一塊兒作一遍dex2oat,這樣能使新的class覆蓋老的class參與dex2oat並完成優化的過程,

2. 中間件資源部署

資源部署基於overlay機制達成,不過dalivk上這個機制並不支持新增資源,在overlay的包裏面若是讀到了一個資源,dalvik系統會去校驗該資源ID在base中值,若是不存在則拋錯,因此動態部署爲了利用這一特性同時支持新增資源的需求,在打基線包的時候就在每一個不一樣的type裏面預留了128個資源ID供後續動態部署使用。

同時打動態部署的patch時會以以前的ID分配的內容做爲輸入,保證已有的資源分配到的ID保持不變,同時若是資源沒有發生變動,則剔除該資源,因此aapt dump resource patch的時候咱們看到的INVALID TYPE 的資源段就表示沒有發生變動的資源,若是有發生了變動,且以前有這個資源,就會在原有的資源ID分配過去,同時新的資源文件打入patch包或者新的資源文本寫入arsc,若是是新增的資源,則使用預留的資源段進行分配。整個替換過程以下圖所示

Atlas動態部署的能力隨着手淘的需求不斷進行着演進,隨着整個框架的開源,天然需求會變得更加普遍。好比在手淘內部一週一發版的節奏下,動態部署對新增component的某些需求並不強烈,而開源後此類功能可能會尤其重要,因此接下來容器的更新迭代中此類的功能也會及時上線。擁抱開源社區會是Atlas從新審視本身,繼續發展的關鍵一步。

將來的路

相比於iOS,Android的推陳出新的速度是極快的。Google在Android系統的改進深刻到方方面面,組件化的發展也須要緊跟Android步伐。

Atlas起步的時候MultiDex還未出來,解決方法數的問題就能帶來足夠的愉悅。如今Atlas基於組件化減小主dex方法數的同時,也支持主dex原生MultiDex的能力。目前Atlas內部集成了MultiDex的能力,外部只須要打開MultiDex開關便可。這麼作的目的一方面避免外部初始化實際的誤解,另外一方面咱們也意識到原生的mulitdex在性能上還有不少優化的點,咱們會在後面及時進行優化。

在開發調試方面,Atlas容器框架基於動態部署的能力支持單個組件的獨立編譯調試,可是相比instantrun的速度,目前還有很大提升。而基於Instant Run改造的單組件秒級編譯也已經在緊鑼密鼓的開發中,後續基於框架的獨立調試插件也將會在Android Studio插件庫中上線。

Android系統機制的演進也鞭策着動態部署前行。好比16年Android 7.0的混合編譯當時也給Atlas形成了不小的挑戰,服務於億級的UV,動態部署的能力的覆蓋面、到達率都還有進一步提高的空間,相比andfix等熱部署修復方案,動態部署須要重啓來打到更新的能力可能也須要有新的轉變。

市面上許許多多的容器框架,基本思路就大體兩三種。Atlas自己也沒有特別新的技術,有的是基於已有的知識進行不斷的重組和優化。在手淘內部,追求更高的穩定性和性能,億級的UV須要及其穩定的容器來承載,同時Atlas也不斷嘗試新的方案,在創新與實用之間不斷權衡。

Atlas是一個結構簡明的容器框架,經過儘可能簡單的思路去解決移動應用從開發到運維中的的桎梏,讓開發中的複雜簡單化,讓維護中的複雜簡單化,讓開發從流水線上忙碌的工做回到專一於對最終目標的精耕細做,拋開嘈雜的干擾,撥開虛無縹緲,到達雲和山的彼端!

掃描下面二維碼直達Atlas項目官網:

活動推薦:

由InfoQ主辦的第二屆GMTC全球移動技術大會開始報名了!大會將於6月9-10日在北京舉行。本屆大會,咱們將探討智能時代的大前端,在動態化、React Native等逐漸流行的如今,移動和前端的融合將會發生怎樣的變化?點擊閱讀原文進入大會官網,6折報名火熱開啓!

相關文章
相關標籤/搜索