閱讀114 返回首頁    go 阿裏雲 go 技術社區[雲棲]


阿裏巴巴開源移動容器化框架Atlas的技術演進之路

摘要:在2017雲棲大會深圳峰會開源專場上,阿裏巴巴手淘技術部資深技術專家倪生華(玄黎)做了題為《Atlas-容器化演進之路》的精彩演講,玄黎從Atlas的發展、特性、技術原理以及開源運作等四個方麵為大家分享了手淘的移動容器化框架Atlas的技術演進之路。麵對All in手淘的航母戰略,如何實現組件化?本文不容錯過。

以下內容根據嘉賓演講視頻以及PPT整理而成。

本次分享將主要分為以下四個部分:
  1. Atlas的發展
  2. Atlas的特性
  3. Atlas的技術原理
  4. Atlas的開源運作
一、Atlas的發展
Atlas開發動力
當大家打開手機淘寶時,可能就會發現阿裏巴巴的手淘的業務其實很大,基本上可以在手淘中看到阿裏巴巴所有的業務,比如聚劃算、天貓等等。特別是在2013年的時候,整個阿裏集團開始all in手淘,手淘成為了阿裏巴巴集團的一艘航母,基本上所有的業務都需要能夠在手淘上運行。
f0cbf39d26f829907685adeb1e9392ce314b2e82
Atlas發展曆程
但是突然發現手淘自己還沒有準備好,那時候手淘基本上一個月發布一個新的版本屬於比較正常的情況,但是當突然多出了100多個業務,而這些業務可能幾天就需要更新一個版本,這樣就出現了平台的發展速度趕不上業務發展速度的情況。除此之外,另一個變化是之前隻有一個團隊進行開發,但是all in手淘戰略提出之後,手機淘寶包含了100多個業務,團隊之間的協作成為了非常大的問題。所以在這樣的需求變得越來越強烈的情況下,手淘非常需要一套框架來支持整個阿裏事業部幾百個團隊的協作以及各自業務的快速迭代。所以從2012年開始,手淘技術團隊就開始嚐試構建移動容器化框架,那個時候的Atlas框架還是基於插件化的設計,進程之間相互隔離,插件彼此獨立,框架完全是一個提供服務的空殼。
a8de4bff0635f20a2d05c8953216a09cae6379fa
插件化架構
插件化的框架策略使用了兩年,後來就發現這樣的策略存在著很多的問題,所以在2014年,手淘技術團隊痛定思痛,完完全全地重寫了一遍Atlas,所以現在大家所看到的開源的Atlas其實就是第二版的Atlas,也就是從原來多進程的方式轉化到單一進程的方式,但是同時也實現了多進程的遍曆係統,支持業務的獨立開發、獨立部署、獨立運行。到2015年的時候,基於業務的需求重新做了按需加載,實現了對於遠程組件的支持,並且對於容器進行了升級。所以從2015年到2016年,Atlas在阿裏巴巴內部穩定運行了兩年,而在207年初的時候之前正式開源了Atlas這個項目。
48df505b133e5a78e864a22866d57b97406da456
插件化所帶來的問題
接下來首先介紹一下Atlas在2012年時的插件化架構設計。如下圖所示的就是當時Atlas插件化的架構,最底層是容器進程:ContainerActivity與ContainerService,在這之上會存在各種各樣獨立的插件進程,比方說天貓、聚劃算等等。這些插件進程分別都有各自的生命周期,獨立地進行管理。為了實現這樣的架構就需要在係統層實現很多的事情,也就是基本上係統所要做的進程管理,Atlas 也都要完成。當時插件化的架構設計所帶來的好處就是插件之間的隔離,包括內存調度等都進行了完全的隔離;除此之外就是不需要再次開發,有新的業務進來可以部署並且直接運行。這樣的架構設計大概運行了兩年,其實也才真正完成了一年多時間就發現了一些非常嚴重的問題。第一個問題就是因為形成了整個物理的隔離,所有的APK都是隔離的,但是手機卻不是隔離的,用戶會將這些APK都放在同一個包裏麵。一個簡單的例子就是天貓引進了一個庫,聚劃算也引進了這個庫,在兩個APK包裏麵,並且可能有很多代碼屬於這個庫的,而這種情況可能出現的比較多,所以代碼的可用性就會非常非常差,這樣就會對於後麵的統一調度以及管控上麵造成非常不利的情況。第二方麵,因為Atlas 插件化的架構設計是基於進程式的,大家都知道進程與線程相比更加重量級,所以使用插件式的方案要比原生的不采用插件式的方案的內存消耗要更大。第三部分是因為要在係統之上隔一層,所以整體的插件適配情況會比較差,插件每發布一個版本甚至每個廠家發布一個版本都需要去重新進行識別。
72e10526e4e31a9bff5499740768b9c5abd5672b
組件化的思考
這樣的插件化架構運行了兩年的時間,後來團隊發現簡直痛不欲生,所以在2014年初的時候整個團隊就開始重新思考手淘到底需要什麼樣的架構。基於之前整個插件化的方案,在經過了深入的思考後認為:
  • 首先,手淘需要高複用性,手淘不是一個大雜燴,需要進行統一地管控,對於所有的中間件、所有的服務都需要有一方進行統一管控。
  • 第二方麵就是要做到低侵入性,對於開發者而言,他們不需要感知容器的需求,隻需要進行編碼就可以了,這就是低侵入性,也就是開發者不需要對於係統由特別深刻的了解就可以運用大量的黑科技,比如說開發者並不需要了解安卓係統底層設計,而隻需要在安卓容器上使用Java層的API就可以完成自己想要實現的功能。
  • 第三個方麵就是高可用性,高可用性也好,性能也好,要保證使用了插件不能對於應用的性能產生太大的損耗。
  • 最後一個方麵也是業務插件方提出的請求,就是希望業務和業務之間使用插件隔離開來,獨立地進行開發和調試。業務今天可以跟著手淘的航空母艦一起跑,未來也可以自己獨立地運行。
在2014年初的時候,手淘技術團隊在想清楚這四條原則之後,大概花費了一個月的時間構建出了新版的Atlas框架體係。
83785a3f8455859ef04d7d3d25966a1fe85f3767
Atlas體係
如下圖所示就是現在的Atlas體係結構,在整個容器中沒有進行混淆之前不會超過100個類。像圖中最下麵的一層,稱之為Hack層,包括OS Hack toolkit & verifier,這裏其實主要是為了對係統能力做一些擴展,然後做一些安全校驗。上麵的一層是Bundle Franework,就是我們的容器基礎框架,這一層仿照整個OSG2層容器的概念定義了一層所有容器插件化的生命周期提供Bundle管理、加載、生命周期、安全等一些最基本的能力。再之上的一層是運行期管理層,主要在容器的運行時,對於所有Bundle的版本控製進行管理;以及清單,會把所有的Bundle和它們的能力列在一個清單上,在調用時方便查找,這樣就可以知道到底有多少個容器;這一層還存在對於類加載器以及資源的代理,這裏就是和業界一些插件化框架機製類似的地方,Atlas會代理係統的運行環境,讓Bundle運行在自己的容器框架上。這一層最右邊的則是整個運行期間的監控器,可以監控運行的情況,也方便了工程期開發調試。最上麵一層就是業務,這一層是麵向開發者的,開發者隻需要知道存在AtlasBridgeApplication就可完全去使用容器的能力,除此之外還有Tools,可以用來進行校驗和判斷。這樣帶來情況是使得組件開發與在後台的開發是一模一樣的,經過一個代理的Resource和Classloader進行操作。而這些東西對於組件開發而言,完全不需要深入了解,這部分內容會在之後的章節會繼續展開進行分享。
41fb5705ba6ecd6fb83b658ebc56b1530d612a02
Atlas業務分層
Atlas的整體業務分層如下圖所示。底層稱之為服務層或者是宿主APP,大家都知道很多應用會引入第三方的中間件或者組件,將這些組件放在基礎服務層,也就是上層的業務統一會使用的業務能力層,而這一層的業務能力不會直接去調用應用模塊。在服務層之上就是各自的業務模塊,這些業務模塊可以和技術服務層進行交互,也可以和其他業務模塊通過協議的模式進行交互。但是如果業務模塊需要使用到一些公共的業務模塊,就可以抽離出一層偏業務的服務。
35317230ebf0a597ed2933068ce52f8967f733bc
基於整個Atlas的容器,手淘做到了什麼樣的程度呢?大概是從2013年開始,手淘開始開發Atlas容器框架,到2014年正式成立。下圖中左邊的這張圖就顯示了手淘Android端的發布次數,從2013年發布了42次增長到2016年發布了600多次,這裏的版本是指的真實交付到用戶客戶端上麵的。右麵這張圖是從手淘團隊從2013年開始到2016年為止,發布一個新版本所需要的時間情況,大家可以看到在2013年發布一個新的版本需要平均10天以上的時間才能交付給用戶,而到了2016年幾乎一兩天就可以交付給用戶一個新的版本,可以認為手淘的業務每天都在發生著更新。在這樣的快速迭代之上,目前整個手淘大概會有70多個備案明確的業務;對於人員而言,手機淘寶自己的部門大概會有400多個工程師,而整個阿裏係裏麵還有20多個BU在參與其中。這些在Android方麵的基礎都是基於Atlas來做的,基於Atlas才能實現對於綜合業務的快速支持。

Atlas帶來的價值
1dcbf5fc19825b3b67be4943682b7e5fddce04ec

二、Atlas的特性
在第一個章節介紹了Atlas的整體發展曆程,Atlas的發展其實是基於整個業務的快速迭代和分工協作進行的,接下來為大家分享Atlas的一些特性。從2015年開始Atlas進行了組件化,整體進行了更新,擁有了很多新的特性。

對於Atlas的特性而言,可以大致如下圖所示。首先,Atlas對於開發者而言是無侵入性的,沒有任何衝突,在開發過程中不需要做任何感知,開發者原來是怎樣開發的現在依然是怎樣開發。第二個特性就是組件是獨立進行開發和調試的,組件的開發可以不依賴於其他模塊。第三個特性就是高兼容性和高穩定性,Atlas從開始運行到今年位置在阿裏實際運行了4年多,組件化設計從2015年開始到現在已經運行了2年多,扛過了兩年的雙11。同時它具備動態更新能力,所謂動態更新能力就是目前Atlas可以支持組件獨立開發,Atlas兼容從Android 4.X一直到Android 8,而且對於市麵上所知道的ROM都是兼容的,而且目前沒有發現任何一個ROM在手機上麵會出現問題。目前手淘已經穩定運行了2年多,整體手淘的Crash率一直維持在萬分之五左右,因為容器導致的crash占比小於百分之一,而手淘Atlas的性能損耗在5%之內,也就是使用容器和不使用容器加載方式來點擊的性能損耗在5%以內。
5bf44347f75ca7ba09139227089f5583a54237b8
對於Atlas的特性而言,在另一個角度上將其分為兩部分。第一方麵Atlas可以做到工程師獨立,在所有的開發過程中都可以做到獨立開發、獨立調試、獨立集成以及獨立發布,也就是說業務開發可以完全脫離手淘,可以認為沒有這個大的IP存在,就像一個Web應用一樣將其放上去,甚至不用去找應用市場進行信息發布。這就是在整個工程化體係上麵每個Bundle獨立開發以及獨立發布。第二個方麵是在整個運行期間,之前提到了本質上是基於OS G2去做的,因為Atlas實現了資源的隔離和aapt分段,運行期中在Bundle和Bundle之間也實現了資源的隔離。所以總體而言,Atlas可以實現透明,靈活,穩定,敏捷,這同樣也是在最開始構建Atlas時所提出的四個要求。
93f268751f49e21f835d795a66c95c9a54c113b6
Atlas與其他組件化方案的對比
在下圖中將市麵上所有組件化解決方案進行了羅列。對於組件化方案可以進行對比去看,不一定有好壞之分,隻是適不適合某個特定的場景,比如對於需要對組件進行獨立支持和獨立加載的APK適合什麼樣的組件化方案,Atlas就不支持這樣加載獨立的APK,如果需要支持加載Web APK或者第三方的APK可能選擇Atlas並不合適。第二點,Atlas支持動態更新所謂的插件宿主,也就是宿主容器中間件這個層麵也是支持動態更新的。第三點就是對於四大組件的動態啟動,現在對於四大組件的動態增加Atlas是部分支持的。對於首次插件啟動的性能而言,如果插件引用了容器化或者組件化的方案,那麼在插件首次啟動時,性能都會有一定的影響,這是因為引入這樣的方案會將資源和類的加載過程向後推。在Atlas中對於插件首次啟動的性能進行了優化,所以用戶可以感知到插件啟動是非常快速的,這個優化是基於係統層麵的,並且是基於一個進程的,所以物理隔離是沒有做的,如果相關組件掛掉了,那麼這個插件也會掛掉。而且Atlas的hook的程度其實是非常輕的。對於代碼運行隔離而言,其實因為Atlas的整體運行機製基本上可以做到組件和組件之間的隔離,目前非侵入式這些Atlas都是可以做到的。對於插件的懶加載,Atlas采用的策略是隻有在用到插件時才會進行加載,一個像手淘這樣的應用可能會有70多個模塊,但是用戶可能並不會全部使用到,所以沒有必要在一開始全部加載進來,隻需要在用戶要使用的時候把模塊加載進來就可以,比如用戶在手淘中點擊了天貓或者聚劃算,才會將這些插件啟動起來,不使用時不會啟動。其實下圖中標紅的部分也就是與其他的組件化方案相比,Atlas的一些特點,如果這些特點正好符合應用的需求,就可以嚐試使用Atlas,如果這些特點與需求相差甚遠,可能Atlas就並不適合這樣的場景。
4c82294dbfa5b09a606f182dc5b5c4ceb7b0cdc5
Atlas動態能力
以上是在組件化的方案上進行對比,而現在比較火的就是動態更新能力。其實在阿裏運行的最多的還是基於Atlas這一套的東西,接下來就為大家分享一下Atlas的動態能力。這部分從六個方麵來看:
  1. 支持類型,目前Atlas支持Class文件,SO和資源文件的增刪改操作,可以說是全類型的動態能力。
  2. 兼容性,Atlas適配Android 4.x - 7.x版本,而且已經在線上穩定運行了2年多的時間。
  3. 高性能,通過去Verify等手段,達到極小的性能損耗。
  4. 補丁大小,Atlas自己有一套Cache代碼的git,包括OS、資源文件的利用,通過精細化Diff的方法,達到非常小的Patch包,手淘在增加業務時包的大小也不會也太大增加。
  5. 成功率,目前成功率還是非常高的,與業界相比,具有較高的部署成功率和效率,目前影響成功的最大因素就是用戶的磁盤空間。
  6. 開發透明,這一點也就是對於開發者友好。如果開發者需要去開發動態部署的能力或者動態更新的能力,其實不需要去關心應用是不是動態更新的,隻需要在業務代碼裏提交一個新的版本,Atlas會幫助開發者自動生成Diff包。
0c867a94669377904220cb38ef4966ceb085e908
目前存在的局限也可能是所有動態更新中都會存在的局限,就是暫時不支持Manifest中的權限等內容,如果涉及到Manifest裏麵的權限就需要做係統的注冊,所以這些Atlas還是不支持的;而且Atlas對於Activity,Service的新增也是有限支持的。

三、Atlas的技術原理
上麵就主要為大家介紹了Atlas的兩個特性:組件化和周邊化。接下來會為大家簡單地介紹一些Atlas的整個技術原理。

類加載機製
組件化也好,動態部署也好,都需要進行類加載來完成這樣的事情。大家都知道所有的類加載都是基於ClassLoader來執行的,Atlas的核心就是在每個Bundle下麵去設置一層BundleClassLoader,Bundle類加載器的邏輯則是在Bundle進行查找的時候首先會去查找自己的Bundle裏麵有沒有這個類,如果有的話就不會再去尋找,如果沒有就會去找Host,也就是整個宿主類的Classloader裏麵有沒有這個類,如果還沒有就會去係統或者其所依賴的關係中去找尋這個類。對於下麵整張圖而言,其實原理非常簡單,還是Classloader雙親委托這樣的一件事情,Atlas隻是在Android和Java的基礎之上設計了兩層的Classloader,通過自己定義的Classloader的加載規則來實現對於整個宿主的隔離,從這個意義上講是比較簡單的。
4ad4f724586fc2e80c57f36d08b11c7c9777340d

資源處理機製
除了類之外,開發過程中比較關心的就是資源問題。那麼Atlas的資源處理機製時怎樣做的呢?其實也非常簡單,但是還是會與ClassLoader有所區別的。在Android某些係統上麵將資源進行隔離時會存在一定的兼容性問題,所以Atlas采用了如下圖中右圖所示的另外一種做法,把所有的Bundle資源在編譯期進行了隔離,每個Bundle的資源段都會通過AP去進行修改,給出一個統一的資源字段,通過在加載的時候將每個Bundle的資源放到AssetsManager裏麵。Atlas沒有做運行時的隔離,而是通過編譯期的資源分段模式實現了資源隔離的方案,所以在Bundle裏麵可以使用到Bundle自身的資源也可以使用到宿主的資源。
86cb4ad00aeb02614ca2833ab7e4c813a420c3fe

動態更新能力
目前Atlas動態更新支持三種模式:
  • 第一種模式指的是組件遠程下載。當拿到這個需求的時候,大家可能都會比較慌,有一個Bundle特別大可能無法裝入到APK裏麵,而在Atlas裏麵其實做的事情也很簡單,它隻是編譯的過程中讓這個Bundle參與編譯,但是在最後發布過程中將這個Bundle去除掉,在需要它運行的時候從遠端把這個Bundle下載下來。這種方式使用的比較多的地方就是手淘的預裝包,一般現在的APK都會非常大,通過這種方式可以將主要功能放入到UPDATE APK裏麵去,一些次要的功能可以通過遠程包配置,當這個Bundle啟動的時候再去下載遠程包,這就可以認為是從無到有。當然這並不是嚴格意義上的從無到有,因為在編譯的過程中已經將所有的Manifest都已經打包進去了,次要的功能通過遠程下載的方式進行配置。
  • 第二種動態加載的模式是業務組件動態更新。因為整個宿主類或者中間層的服務類本身的變動一般而言都是比較少的,基本上這些類不會每天都發生更新變化,應對這樣的需求可以采用業務組件動態更新,因為可以認為是完全獨立的ClassLoader進程,可以認為是剝離在整個Android係統的進程之外的,可以實現什麼時候需要就去加載什麼樣的資源。業務組件的動態更新其實是通過組件Diff和Merge的過程進行更新的,並且這樣使直接在組件的生命周期內做,兼容性極好。因為不會涉及到係統操作,隻是一個資源虛化,原來是直接加載資源,現在是Patch下來之後直接生成一個新的資源,這種方式的兼容性會非常好。
  • 第三縱動態加載模式是如果真的需要修改宿主類時就回歸到常見的通過類、資源、SO文件等的diff算法,通過merge算法來實現更新,有比較好的兼容性。
40e61f9a8b2b9ba7374d48fa7d3dafc25e2399cb
Bundle的部署
下圖是現在Bundle部署的diff過程。在進行Bundle打包的時候可能會打一份源代碼,發布哪個就會在倉庫裏麵存儲一個版本,第二次發布的打了一個新的包,並對每個資源進行diff,比如class與class之間通過dex格式形成一個新的classes.dex。Resource與Resource之間可能會去基於MD5進行diff。而對於research和assert會基於Dex merge的模式進行diff。對於資源文件也是一樣,基於手機上的文件和Patch文件生成一個新的資源合集,基於所有的Patch會生成新的運營期的APK文件的容器模型。今天可能發布一個Patch明天還會發布一個Patch,為了支持Patch最小化就需要使用增量發布。增量發布能夠使得用戶隻要更新了一次,下次再進行更新隻要發生變化的資源和類就可以了,不需要將所有的從源文件到類都更新,這樣就可以保證更新量比較小,速度也比較快。
b3e981e5a95caff96daafecdc32de60b01b18921
目錄結構
下圖是Atlas在手機上麵的目錄結構。其實在每個Bundle下都會各自有一個目錄結構,第一次發布完成之後會合成一個version 1,在後麵patch目錄中會合成一個patch 1,當發布了第二個版本時,會在運行的時候去取Version 2這個版本。由於基於目錄結構來做的,所以回滾操作也會非常簡單。所以如果基於動態部署來實現,更新版本後如果發現有問題,隻需要在服務端下發一個命令就可以刪掉或者回滾,而不需要用戶進行操作。在服務端也存在清單的概念,所以可以精確感知到哪幾個版本或者模塊是存在問題的。所以基於這套機製,客戶端的發布和之前服務端的發布是一模一樣的,開發者可以去隨時發布或者隨時回滾,大大地提升了發布和回滾的效率,一個命令下去可能幾秒鍾之內就能夠回滾到上一版本或者之前的版本上去。
9d2c331398e656bbc9289a48e929d4e447cfd479
中間件代碼部署
下圖展示的是Atlas為了規避性能損失的一個措施,其實在整個容器這部分,因為Android的限製需要根據classLoader來更新,會使用插裝的方式和Verify機製。而現在發現基於係統的dex機製可以不使用插裝的機製,所以現在Atlas的做法是用戶手機上麵設置多個dex,如果新的dex和原來是重複的,就會將重複的dex刪除掉,自動合並到新的dex裏麵去,這樣做的好處是這個機製完全符合穀歌的Manifest規則體係的,其次因為取消了Verify機製,整體的性能會非常快。
924e64550a1ee6e9ea5965e798a7e67e741c302e
宿主資源部署
因為安卓本身有一個限製,所有的資源必須得在base包裏,新增一個資源是不生效的。所以一個做法是在打包的時候預留很多空資源。另外更新已有的資源則通過資源覆蓋來完成。在宿主中會預留一些空資源段,當新增的時候就可以適應這些主dex預留的資源段。也就是如下圖中顯示的會有很長一段null的資源,如果增加了資源就需要對於這些標記為null的資源段進行更新。
d612a42625f1e744f429d8e79ba971dc8a16f82a

四、Atlas的開源運作
到目前為止,手淘技術團隊有一個專職的開發團隊去維護Atlas。目前整個阿裏係能夠看得到的使用了Atlas的應用有很多,而且目前阿裏巴巴對內和對外的Atlas是一套代碼,所以大家不必擔心Atlas開源之後不會進行維護了。
86aa9d5803378d2c8e0369f88bdc0945ca77c6f5
下圖展示的就是Atlas未來的一些版本計劃。接下來首先還需要繼續提升Atlas的自身穩定性以及兼容性。其次,因為開源了Atlas,所以也需要降低接入的成本。大概6月份,希望能夠提供出IDE快速調試開發插件,來完善Atlas工具鏈。並希望新增支持動態Service/適配Android O。大概在9月份Atlas會提供極簡接入工具,並對於動態部署擴展(小patch方式)/dexmerge的算法進行改進。
2a766f4386bd3762864a82aeeef49f1742dd04f4

最後更新:2017-05-22 21:31:23

  上一篇:go  業務技術協同線上化的硬盤式研發管理實踐
  下一篇:go  Java基本數據類型的裝換