目前携程大部分订单已来自移动端,App 几乎承载了整个集团的所有业务形态。在内部研发中,携程的 App 已经发展成为拥有 90+
Native Bundle、100+ Hybrid Bundle、30+ React Native Bundle,几百名研发人员,每个版本(1
个月)交付 4000+个 App 包,Hybrid/React Native/HotFix/Bundle 发布次数
500+。如果没有一个有效的无线持续交付平台,很难实现大版本的集成发布在 3 天内完成。而对比市场上开源的无线持续集成工具
Fastlane、TestFlight、Jenkins
都存在各种定制化需求的问题。因此我们从零开始,逐步打造适合携程研发流程的无线交付平台,系统化地解决研发支撑痛点。下面将从集成、测试、发布、运营四个子平台来展开,具体分享我们是如何一步步打造无线持续交付平台的。
集成平台
从最初到现在,携程无线持续交付模型经历了从
1.0 到 2.0 的迭代演进。在 1.0 之前,App 集成和发布还主要依靠人工操作
Jenkins,需要由特定的打包人员负责打包,再将包通过 IM/邮件等方式传递给其他测试人员,测试结果需要专人手工回收,以把控 App
质量。此时最大的问题就是 App 管理混乱,人工介入过多,每次发布都需要花费很长的时间。
1.0 阶段
在 1.0
阶段,我们引入 MCD(Mobile Continues
Delivery)平台思路,将打包人员的工作交给平台来完成,提高了发布工作效率。这时不需要专职人员负责出包,平台会定时自动打包,测试人员可以到平台上自由取包(通过下载链接或扫描二维码的方式)进行测试。与此同时,测试人员也可以在平台上进行单独的打包和测试。测试结果会统一反馈到平台上来,协调人员在平台根据各家的反馈结果决定需要重新出包还是继续下一步发布操作。
在这个阶段,App
的打包还完全依赖于源代码进行,由平台生成打包参数(主要包含 App 运行环境、与 iOS
签名相关的参数以及代码仓库相关的参数)提交给后台的打包系统。它会根据仓库地址和 commit ID
从代码仓库中拉取全量代码,然后打包系统再调用代码中预先准备好的 Build 脚本构建 App
产物,构建完成后将结果保存至临时的文件服务中,最后由平台的回收进程将构建结果回收并处理之后放在云存储上供用户下载使用。
2.0 阶段
1.0 阶段虽然已经基本实现了集成打包的自动化,但是还存在以下几点不足:
MCD 系统发起 Build 请求之后会有另一个定时的 Job 任务去轮询 Build Server 查看 Build
结果。在初期阶段还能满足业务需求,但是后来由于打包数量的增加以及打包频率的提高,系统的处理效率变得越来越低。一方面打包资源不够(Android
打包使用 Linux 虚拟机,iOS 打包使用 Mac),另一方面轮询 Job 的处理效率达到了瓶颈。打包机器采用 Jenkins
方式进行管理,因此很容易进行横向扩展,但是 Job 却很难扩容。
针对以上问题,我们对平台进行了升级改造,主要为:
消息机制的改造:
首先基本打包架构和流程保持不变,在 MCD 系统和 Build Server
之间增加消息系统,MCD 发起 Build 之后不再轮询 Build Server,而是消费由 Build Server 产生的 Build
完成消息,如图 1 所示。使用这样的生产消费模型 MCD 可以轻易地进行水平扩展,系统执行效率得到极大改善。
图 1 Bundle 打包
工程拆分:
App
工程拆分成多个不同的 Bundle 模块,Bundle 之间存在依赖关系。在这个情况下 App 的编译和打包也可以按照 Bundle
的方式进行组织。在开发阶段,开发人员提交完代码之后就能直接将自己负责的 Bundle 编译成可执行文件,测试可以自由选择 Bundle
进行打包。此时打包工作只需将多个 Bundle 文件组装在一起就行,极大地加快了打包速度。通过测试的 Bundle 会被发布到指定地点,在
App 集成阶段只需把所有发布的 Bundle 组装成最终 App 供大家测试即可。
在开发自测阶段,开发人员提交完代码后在 MCD
平台上 Build 出所开发的 Bundle(标记为 L,表示 Latest)。相关测试人员(或开发人员自己)可以打测试包,测试包会使用所有
Bunde 的 L 版本进行构建。构建完成之后获取测试包进行功能测试,测试通过后就可以将该 Bundle 进行发布操作,即标记为 RC
版本(表示 Release Candidate)。
图 2 测试打包发布
如图
2 所示,在集成测试阶段,MCD 平台会自动选取已发布(RC 版本)的 Bundle 打出集成包,测试完成后测试人员可以将测试结果反馈到
MCD 平台中,待测试全部通过之后就可以安排 App 进入发布定版阶段,再进行一些后续市场包操作就可提交给 App 应用市场供用户下载。
由于 App 的开发是个连续过程,测试包和集成包都可以很方便地配置是需要 Hourly build 还是 Daily build,并且可以和测试平台的相关功能联动,从而实现持续集成。
我们也会将不同版本(不同功能)的
App 按照项目进行划分,主版 App 为标准项目,非主版(渠道/HotFix/Bundle)App 为非标准项目,同一项目内的 Bundle
可以自由组合,项目之间互相独立,不受影响。一个项目在创建时可以选择从另一个项目继承其各模块的最新版和发布版。项目在开发过程中也可以从其父项目中实时同步对应模块的最新版和发布版,这样就能基本满足各种开发和测试的需求。
测试平台
随着携程持续交付的发展,原有的测试模式以及流程渐渐显现出不足,主要体现在以下三点:
-
快速验证与手工验证的冲突;
-
自动化回归测试与手动测试效率的矛盾;
-
设备兼容测试与设备不足的矛盾。
快速验证与白屏检测
在集成测试阶段,每一次出包首先会由基础测试人员负责验证此次出包的质量,主要是验证各个 BU
入口页面 Hybrid 或直连页面是否正常,如果大量 BU
入口页面异常则会要求重新出包。这个过程看似很简单,但是“(沟通成本+验证成本)*出包次数”所耗费的时间也是不容忽视的,且这种冒烟测试对于测试人员也很枯燥无趣。
图 3 集成包
针对以上情况,最初引入了白屏测试等基础性测试功能,并与集成平台打通(见图
3,可以直接从集成包列表选择相关测试功能)。白屏测试主要是检测 App
首页各个入口页面是否显示正常,对于每一个页面会进行图像比对以及页面长度来验证页面是否正常,白屏会在多个设备进行测试,只要其中一台设备通过,则认为这个入口是正常的。
自动化回归测试
通过回归测试自动化,可以提高回归测试效率,有效缓解测试人员的压力。我们逐步开发了 MCD 测试平台用于自动化测试项目的管理、执行、报告和展示,支持 Android/iOS App、HTML5/Hybrid Testing。
图 4 测试平台架构
测试平台架构简化图如图 4 所示,各个模块主要功能如下:
-
Portal 负责与用户交互,查看报表,保持后台系统对用户透明;
-
Invoker 是调度通道,用户基于 Portal 提交的测试任务通过此通道来调度,包括创建项目、设备占用、测试项目执行等;
-
Lab Center 负责所有与设备相关的业务,Lab Center 与二次开发的开源软件 STF 交互,所有设备挂载在 STF 上;
-
当具体的测试项目在 Jenkins 上执行时,会将预先占用的设备远程连接到 Slave 上等待使用。
MCD 测试平台提供了自动化测试(白屏检测、录制回放、回归测试)、系统测试(兼容性、性能、稳定性)、手动测试(远程租用、远程调试)三大类功能,基本达到了我们对于提高测试效率的需求,如图 5、图 6 所示。
图 5 Mobile 项目列表
图 6 自动化测试 Task Case 页面
另外,日常测试中还经常遇到这些情况:需要快速验证
App 的一个功能,有些问题只能在特定机型上重现,测试目前却没有这款手机。因此我们基于 STF
二次开发出设备共享平台,将空闲设备收集起来在平台上共享,用户只需在平台上搜索需要的设备就可以立刻通过 STF 进行使用了,如图 7 所示。
图 7 设备租用
同时,我们还建立了代码质量的自动化,集成 Sonar、Facebook Infer 和 Uint Test 功能,方便每个版本对代码质量进行跟踪,如图 8 所示。
图 8 代码质量自动化追踪
发布平台
无线发布系统一直以来都是无线应用能快速迭代最关键的一环,一个好的发布系统能帮助开发快速地修复线上问题,并能快速将新功能投送至用户,帮助业务抢占市场,而发布系统的设计也随着 CD(Continuous Delivery)概念的深入人心而日新月异。
无线发布的特点是静态资源包(不管是 Hybrid、React Native 还是 HotFix、Bundle),发布技术中涉及到的主要问题如下:
资源包发布
针对静态资源包发布,经历了如下几个历程:
-
全量包发布:
携程最初使用的都是全量发布的方式,这种方式实现简单,但也简单暴力,而问题也比较明显,导致用户升级时流量巨大。
-
文件字符串差分:
这种方法避免了整包差分的问题,能够比较方便地实现快速迭代要求下的小量更新需求,并且文本差分工具简单,实现容易,出错较少,但缺点是差分效果差。
-
文件二进制差分:
对每个文件进行二进制级别的差分能够具备小量快跑的特点,同时进一步优化差分文件 Size。这也是携程目前使用的方式,它仍然使用了 bsdiff 作为差分工具,当文件没有更新时,不输出差分文件,而更新时也只产生 Size 非常小的 diff 文件。
-
按需差分:
在按需差分前,我们采取的是预差分方案。就是在发布时,把所有的版本间差分准备好,有多少个历史版本就将产生多少个差分包。按照这种做法,随着发布次数的增多,发布差分包数量也将急剧增长,对存储的要求越来越高,也造成了浪费。
在此前提下,引入了按需差分,就是终端用户通过将自己的版本号与线上最新版本号做比较,发现落后时,触发差分过程,从而获取增量更新。