今天手淘技术团队宣布正式开源它们的容器框架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折报名火热开启!