根据“墨菲定律”:如果事情有变坏的可能,不管这种可能性有多小,它总会发生。即便我们在发布管控和线上监控上做的再充分,线上问题最终还是无法避免的。所以当通过线上告、客诉等手段发现线上问题之后,我们需要及时的应对问题、解决问题,把问题带来的影响降低到最小,并以最快的速度恢复对用户的服务。

在有效应对的方面,我们主要靠两种手段。第一种是存在B方案兜底的情况,使用Horn灰度配置,关掉MRN开关,短时间内恢复成Native页面或者H5页面继续为用户提供服务,同时通知相关RD和QA快速定位问题,及时修复,验证并上线。第二种是无兜底方案的情况,CDN服务器(Eva)上撤掉问题Bundle,实现版本回滚,接下来的问题定位过程跟手段保持一致。

这两种备案保障了外卖MRN业务的整体高可用性。

(5)复盘改进

在以上四个大环节中,问题可能会出现在任意一个环节。除了及时发现问题与解决问题,我们还需要尽力避免问题。这一点主要是靠我们内部的例会、复盘会,对典型问题进行Review,将问题进行归类,包括复盘流程规范问题、操作失误问题、框架Bug等,并力图通过规范流程、系统优化来尽力地避免问题。

在外卖MRN项目实施过程中,我们共推动了二十多项规范流程、系统优化等措施,大大保障了整体服务的稳定性。

最后,我们用一张图对外卖监控运维体系做一个总结,帮助大家有一个全局的认知。

混合式架构流程

针对混合式架构的流程,目前外卖技术团队采用的是正常双周版本迭代流程+周迭代上线流程。MRN页面既可以跟版迭代,也可以不跟版迭代,这样可以有效地减少流程的复杂度和降低QA的测试成本,而周迭代流程可以有效地利用MRN动态发版的灵活性。混合式开发和原生开发应尽量保持时间节点和已有流程的一致。这种设计的好处在于,一方面随着动态化的比例越来越高,版本迭代将可以无限拉长,另一方面从双周迭代逐渐演变成周迭代的切换成本也得到大幅的降低。详细可分为下面几个阶段:

评审阶段

业务评审阶段在原有的流程上,增加了技术选型阶段。在技术选型时,明确是否会存在需要使用MRN页面的情况,如果页面可以完全不涉及到Native部分即可完成,就可以进入周迭代的发版流程。如果需求用MRN实现,但是又涉及到Native部分,仍然走周迭代的上线流程。除正常开发需求的时间外,RD需综合考虑到双端上的适配成本。

开发阶段

客户端以周维度进行开发,每周确定下周可提测的内容,根据提测内容是否为动态化的业务、下周是否在版本迭代周期内,决定跟版发布或周发布。

提测阶段

提测前,为了保证MRN页面的提测质量,RD首先需要按照QA提供的测试用例提前发现适配问题。提测时需要在提测邮件中注明:(1)提测的Bundle名称和对应的版本号 ;(2)标明哪些组件涉及Native模块 ;(3)依赖变更情况,如是否升级了基础库,升级后的影响范围;(4) 重点测试点的建议。

上线阶段

MRN由于其可动态发布的特性可以跟版发布,也可不跟版发布,但上线时间和灰度时间节点都保持了一致。不过版本还是动态发版,都默认周二上线,周四全量。

跟版发布:默认只对当前版本生效,需在双周迭代三轮提测节点,周二当天将Bundle上线服务器,MRN的灰度开关全量打开。通过周四App的发版灰度比例来控制MRN的灰度比例,上线时需配置报警和灰度助手监控,实时掌握MRN的线上数据。 不跟版发布:也同样以周四作为全量发布窗口,Bundle需在周二时上线指定线上版本,指定QA白名单。测试通过后,在周三按照比例逐步灰度,周四正式全量,和跟版发布一样,上线时需要配置报警和监控。

架构总结

引入MRN后,相对单平台而言,架构层级上,我们增加了2个MRN中间层去屏蔽Android和iOS平台、原生组件之间的差异。这样做的目的是为了让上层业务开发者可以很快地使用框架进行业务开发,完全不用关心平台和组件间的差异。通过引入MRN技术栈,带来的好处很明显:

(1)使用MRN实现的页面理论上可以实现一套代码,部署到不同平台上,开发效率得到大幅度提升。 (2)采用MRN框架,无论是加载性能还是页面滑动性的用户体验上,都会比原来H5的方式要好。 (3)部分页面具备了快速编译、快速发布的能力。

但一个硬币总有两面,混合式架构增加了架构的复杂度,使得原本只要考虑一个平台的事情,逐渐转变成需要考虑三个平台,另外Android本身具备碎片化的问题,这使得混合式架构的适配问题较为突出。当出现问题时,我们的第一反应由“这是什么问题”变成“它是否存在于两个平台,还是只在一个平台上?”、“如果仅在一个平台上,是在原生代码还是React Native代码出了问题?”、“历史版本的MRN是否存在问题,是否需要修复”、“修复的效果在Android和iOS上的表现是否一样”,这些问题增加了定位和修复工作的复杂性。另外,MRN的适应场景也是有限的,并非所有的业务和页面都适合改造成MRN,如何做选择也需要进行有效的判断,从而增加了决策成本。

针对上述问题,我们的建议是:

(1)减少分歧: - 在研发、测试、发布和运维环节,MRN的页面尽可能对齐Native原有的环节,减少团队理解的成本。 - 在Debug开发环境下,利用页面浮层提示技术栈使用情况;Release环境下,利用工具、MRN自动化报表,及时的让开发同学明确知道是Native页面还是MRN页面,减少确认。 - MRN页面尽可能地避免原生组件的使用,而使用纯JS代码实现,供MRN页面使用的原生组件的需要高质量的提供,减少下层组件的问题。 - 默认只修复当前的版本,出现严重问题时才考虑修复历史版本,减少多版本带来的复杂度提升。

(2)技术栈明确边界

做好Native和MRN技术栈使用的边界,尽可能用简单的选型标准,让合适的场景选用合适的技术栈,从而保证业务整体的可用性,让用户体验依然如初。

(3)单技术栈转向多技术栈团队

培养全栈工程师,当团队的同学都具备iOS、Android和MRN多个技术栈能力时,将会有效地提升开发的效率,短期内可选择iOS、Android和MRN工程师结伴编程的策略。

可用性体系

正如在“监控运维”章节中所讲到的那样,线上运维是我们工作的重中之重。这个章节我们就讲一下我们对于监控指标的选取。鉴于外卖业务的特殊性,除了美团的外卖频道之外,外卖业务还需要运行在独立的外卖App上。如下图所示:

外卖App经过多年的发展,目前已逐渐成为一个平台级应用,承接了C端、闪购、跑腿等多个业务。与美团App相比,它们之间在很多基础建设、扩展、网络部分都存在差异。所以在监控核心指标的选取上,我们除了保证C端MRN业务在美团以及外卖两端的高可用性,还需要保证外卖App平台本身基建的稳定性,从而保证运转在外卖App上所有MRN业务的高可用性。

而从监控的大分类上来讲,我们分为了【可用性指标】以及【性能指标】,它们分别关注业务本身的可用性,以及页面的性能与用户体验。接下来,我们就依次进行讲解。

MRN可用性指标

可用性指标也是我们关注的关键指标,它直接决定了我们的MRN页面是否能够正确、稳定地为用户提供服务。通过MRN Bundle加载全景,我们可以确定整个包加载的几个关键节点。可以说,MRN业务的可用性就是取决于这些关键节点的成功率。

下载链路

MRN是一个动态化的框架,所有的MRN Bundle都是从CDN节点上远程下载。所以下载成功是MRN业务可用的先决条件。有些普通的业务方是不需要关注这个指标的,而外卖App可能会因为网络库基建,出现启动下载线程拥堵、DNS劫持等问题,所以我们把下载成功率作为外卖App监控的全局指标。目前,外卖App的下载成功率长期稳定在99.9%左右。

加载链路

加载链路可以细分为初始化引擎部分以及业务Bundle加载部分。前者跟基建有关,代表从引擎创建到加载完Common包加载成功这段的成功率。这部分主要依赖MRN SDK的稳定性,从我们的日报上看,稳定性基本保持在99.99%以上。

而业务Bundle加载成功率(MRN PageLoad Success),是MRN页面创建到业务视图内容渲染过程中,没有发生错误的比例。它与跟拉包时网络情况、MRN框架稳定性和业务JS代码都有关系。这也是我们关注的核心指标,因为它直接决定了我们某个页面是否可以渲染成功,所以我们把这个指标同时列为了外卖App监控告警的全局指标与单Bundle告警的指标。目前,整个外卖业务的Bundle加载成功率稳定在99.9%以上。

使用链路

Bundle加载成功之后,页面成功被渲染。但是在使用的过程中,可能会因为JS代码,Native代码的Bug出现JS Error、Native Crash等问题,这样给用户带来的直观反馈就是应用闪退、页面白屏等,造成了服务的不可用。所以在使用链路上出现问题率,基本也可以直观反映出一个RN页面的质量以及它当前的运行状况。

在使用链路上,我们主要关注的是JS Error率、JS Error个数以及页面退出成功率(MRN PageExit Success)等。

JS Error很好理解,由于RN是由JS驱动的框架,所以一个页面的JS Error率基本上可以综合反映出一个页面的可用性、稳定性或者基建的稳定性,故我们同样把这个指标同时列为了外卖App监控告警的全局指标与单Bundle告警的指标。我们用上报上来的JS Error数量做分子,该页面的PV做分母,计算一个页面的JS错误率,当JS Error个数短时间内极速升高或者JS Error率有大幅上升时,就会触发我们的JS Error告警。目前外卖大盘的JS Error率保持在万分之一左右,略低于Native Crash率。

页面退出成功率(MRN PageExit Success),理解起来不如前面的指标那么简单,因为它表示的是用户在退出MRN页面时,业务视图内容已成功渲染的比例。它会包含所有已知和未知的异常,但是用户进入页面后快速退出的场景,也会被错误的统计在其中,因为用户退出时可能页面尚在加载中。相比于JS Error,它是一个更加综合的指标,基本上涵盖了加载失败、渲染白屏、使用时出现错误等多个异常场景,基本上可以反映出一次MRN业务的单次可用性,相比于之前的指标会更加严格。我们把这个指标同时列为了外卖App监控告警的全局指标与单Bundle告警的指标。我们希望它永远能保持在99.9%以上,否则就会触发告警。目前外卖大盘的MRN PageExit Success基本稳定在万分之三左右,我们最终的目标是希望稳定在万分之一左右。

最后,我们希望通过两个“脑图”快速回顾一下外卖全局监控与单业务监控关注的核心指标。

MRN性能指标

除了可用性指标,性能指标也是我们重点关注的内容。如果加载时间过长,就会大大增加用户离开页面的概率。而页面卡顿,也会影响用户在使用层面的体验,从而引发客诉或者业务损失。

根据Bundle加载全链路图,我们也可以把性能指标分为两个大类,一个是加载时耗时与使用时性能指标。前者主要关注Bundle从Load到渲染整个链路的耗时,后者主要关注使用时的性能指标,在这里主要是指页面的FPS。

加载链路耗时

如上述所说,整个加载链路分为引擎初始化的时间以及Bundle本身加载及渲染的时间的时间。

引擎初始化的时间在整条链路上占比是最长的,因为初始化的时候会加载比一般业务代码大得多的CommonJS。经过观察,这部分的时间总体表现较差,在iOS上50分位和90分位分别是0.3s和0.7s。在Android上表现更差,50分位和90分位分别是1.3s和1.8s。不过目前MRN已经使用了预加载方案,即在App刚启动时就初始化一个JS引擎,等实际使用时,直接复用该引擎即可,大大缩短了首次Bundle的整体加载时间。

页面加载时间和页面渲染时间是我们关注的第二类指标,从加载链路图也可以发现,页面加载时间代表从开始加载Bundle到RN内容渲染成功的整条时间,而页面渲染时间则是它的子集,代表Bundle解析完毕,从JS StartApplication开始加载组件到渲染出第一帧的时间(iOS和Android的统计口径不同)。区分这两项指标也可以更好地分析整个加载链路上的瓶颈在哪,有助于针对性的做性能优化。

以外卖iOS 50分位为例,我们发现页面整体的加载时间在400ms左右,JS渲染时间只需要100ms左右,主要的性能瓶颈在Bundle加载以及JS Bundle的解析部分,这也是我们接下来需要重点研究课题。

使用时FPS

衡量用户使用体验比较直观的一个指标就是FPS,较高的FPS会让用户更加顺畅地体验功能,完成操作。

目前,MRN在外卖侧业务总体落地页面复杂度适中,遇到复杂动画也使用了BindingX来提升性能。通过监控,外卖侧的页面总体表现良好,在iOS上几近满帧,在Android上表现稍差,平均在55帧左右,较深的视图层级与较低的JS-Native的通信效率都是MRN FPS的杀手。如何提升MRN特别是在Android上的页面性能也是我们下一阶段研究的课题。

目前,外卖性能指标50分位的性能指标基本满足线上需求,但是90分位的表现不尽如人意,特别是较低的FPS以及过长的页面加载时间。革命尚未成功,同志仍需努力。

效率衡量

引入MRN,提升了本地的开发效率,但同时也增加了工程的复杂度,所以总体来说真的能提升实际开发效率吗?在完成几十个RN页面的开发后,总结了一些公式,希望可以给其他团队一些结论性的参考。首先设定三个方面去考量:人效提升、代码复用、维护成本衡量,将外卖的所有MRN页面加在一起,取平均值,可以得出较为准确的结论:

人效提升计算公式:∑(Android Native总人日+iOS Native总人日-RN总人日)/ ∑(Android Native总人日+iOS Native总人日) 代码复用率计算公式: ∑(RN行数-平台分支判断代码块)/ ∑(RN行数+Android native+iOS Native) 维护成本计算公式:∑(Android Native原生总行数+iOS Native页面总行数-RN页面总行数)/ ∑(Android Native页面总行数+iOS Native页面总行数)

根据页面的交互程度去进一步的划分,得到如下的表格:

如表所示:人效提升的方面,主要取决于页面是否存在复杂的交互,如果页面存在复杂交互,就会不可避免的导致涉及到Native的双端原生开发,如部分交互需要Native Module实现,最终的人效提升将大打折扣。而对于涉及较少的Native Module和展示型的页面,MRN存在较大优势。但大家会很奇怪这种结果,为什么人效提升会大于50%?逻辑上Android和iOS双端复用后,提升的效率理论上最大应该是50%。这是由于RN bundle的热加载极大地节省了Native的编译时间,这一部分相对原生开发效率大概能提升20%以上,使得最终的人效提升大于50%。双端复用率方面,对于纯展示型的页面,大概率可以完全由JS实现,双端复用率可以达到100%,后续双端只需维护一份JS代码即可,极大的降低了维护成本。对于一些交互复杂的页面,需双端各自封装对应的Native Module实现,复用率下降,维护成本变高。

总结

随着业务的快速发展,工程复杂度的不断提升,在没有外力的情况下,开发效率必然会持续下降。如何在资源有限的情况下不断提升开发效率是一个永恒的话题。美团外卖客户端通过借助美团基建MRN,推动混合式架构来提升效率。截至目前,美团外卖业务已经有60多个RN页面上线,每天的PV高达上千万,为用户提供了稳定可靠的服务。

混合式开发带来的不仅仅是技术层面的挑战,更是对团队成员、团队组织能力的挑战。MRN虽然能够做到跨端,但是有时候仍然需要针对特定平台单独编写代码来解决问题,这就间接要求工程师必须熟悉三个平台,团队也必须有效组织各技术栈人才共同协作,才能真正用好MRN。

参考文献

京东618:RN框架在京东无线端的实践 React Native架构分析 点我达骑手Weex最佳实践 State of React Native 2018 使用React Native的五个理由 iOS 开发是否要采用 React Native 开源React Native组件库beeshell 2.0发布

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频 如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)

由于篇幅原因,这份面试宝典已经被整理成了PDF文档,有需要Android面试宝典全套完整文档的麻烦点赞+点击GitHub即可获取资料免费领取方式!

本文在开源项目:GitHub中已收录,里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…

Android)** [外链图片转存中…(img-GCXP5XkZ-1711201666733)]

由于篇幅原因,这份面试宝典已经被整理成了PDF文档,有需要Android面试宝典全套完整文档的麻烦点赞+点击GitHub即可获取资料免费领取方式!

[外链图片转存中…(img-HNGDocCR-1711201666734)]

本文在开源项目:GitHub中已收录,里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…

好文链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: