天猫客户端稳定性保障以及性能优化实践

1002661109 8年前
   <p>手机天猫全局体验组负责人吴发伟于APMCon 2016移动性能优化专场发表了题为《天猫客户端稳定性保障以及性能优化实践》的演讲,现场解读了天猫客户端在稳定性保障方面的举措,介绍如何从救火式的“消防队”转型到能够及时的发现问题、预防问题的产生,以及解决客户端启动Crash的利器——安全模式是如何打造的。</p>    <h2><strong>架构演进</strong></h2>    <p>一开始天猫客户端每一个端是一个同学在做开发,这个时候大家都很高兴,所有人都愉快地进行开发。但是随着无线占领市场,流量以无线为主,整个行业包括阿里也不例外,天猫无线会面临各种挑战,我们从一个团队负责无线,变成多个团队负责。其次,所有需求必须先在移动端上线,以前PC端长线需求必须转到无线上面去做,并且需求也是成倍增加的。所以这个时候很多同学其实是从后台或者其他的角色转过来做客户端的。相对来说他们就算客户端研发新同学,在这种情况下我们如何保障产品的稳定性呢,这个是我们面临的一个很大的挑战。</p>    <p>为什么说架构上要做改造?第一,支持多团队的研发节奏。第二,负责稳定性同学发现一个问题,如果版本发布要很久,这时远水解不了近火,提升版本的研发迭代速度非常重要。我总结两个词语, <strong>第一个对于我们来说研发效率,第二是用户体验</strong> 。</p>    <p>所以,我们做架构改造之前有这些问题,第一个 <strong>开发阶段冲突非常多</strong> 。因为大家都会同时改一个地方和模块,开发阶段 <strong> 编译时间也是耗时比较 </strong> 久。第二, <strong> 发布周期特别长 </strong> ,这个时候是捆绑式研发模式,只要有任何模块的业务方出现问题,我们整个版本就发布不了了。第三,线上质量怎么样去保证?所以,架构改造势在必行。</p>    <p>做架构改造有一些原则,这里简单介绍一下。</p>    <p>第一,算法导入里面提的分而治之,一个工程说是所有人负责事实上是没有人去具体负责。所以,很重要一个点就是一定要划分模块, <strong>SRP就是The Single Responsibility Principle</strong> ,单一功能职责。</p>    <p>第二,就是业务模块的 <strong>Bundle化</strong> 。天猫业务模型跟大家说一下,这个里面有首页,搜索,店铺,还有购物车,下单,物流等等这么多业务模块。我们架构怎么样去拆分?横向是按照功能拆分,第二,就是按业务划分,把首页的相关电路模块划分到首页团队去,这就是康威定律。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/2c3e091f386f2f0a41d0cfe1bf55c231.jpg"></p>    <p>接下来提一下架构改造用到的一些工具,模块解耦中有跳转协议、Rewrite、Beehive。从首页到商品详情最下面就是更多的惊喜,直接点一下就可以了。我们要保证研发速度快,怎样解除依赖?我们会通过一串URL跳到对应的时候,需要根据报的结构跳转过去。第二点是 <strong> Rewrite </strong> ,我们有多个跳转目的地时,当线上一个地方出现问题,可以通过它来指到另一个目的地。第三,Beehive做模块解耦。</p>    <p>关于依赖管理工具,这里大概画了一个示意图。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/50baaec8e26fac852fb585dbd95af339.jpg"></p>    <p>在改造之前就是这样一个单工程架构,当然架构有一些分层。我们所有的业务都是在上层,UI都是在一个工程里面开发,右边是容器化的架构,各个业务团队可以依赖于这个业务容器进行独立的开发,研发好测试通过就可以及时地进行发布。通过这样的改造,可以做到按业务进行快速迭代的能力。改造之前,大家都是捆绑式的,但是,做了这个架构改造以后,我们就支持多团队开发并且能够做到想发布随时发布,随时进行拉分支,把要改动的业务模块做一个集成。因为我们各个SDK都是有版本的管理,如果说有问题,我们把它弄掉就可以了。</p>    <p>中间的BUS层就是跳转协议,还有Rewrite,Beehive。同时,我们也对之前的一些逻辑,包括一些辅助功能,把它抽离中间键,将一部分功能做了合并,这样有利于安装包缩小。</p>    <h2><strong>稳定性保障</strong></h2>    <p>稳定性保障,上一个架构改造举措目的就是提升迭代速度,一开始负责稳定性的时候就会比较痛苦。举一个例子,当线上出问题的时候,你现在修改了,发布版本需要至少几天甚至两周的时间。如果真的是很重大的问题,像你负责的APP到消费者手中因为缓存没有办法启动了?怎么办?有没有同学碰到这样的问题?所以说负责稳定性同学很辛苦,他们默默做很多的工作,应该多鼓励一下。</p>    <p>我们一开始也是碰到这个情况,面对这种方式最开始我们觉得就是采取救火式,着火了我们就去解决,其实应该建立比较好的机制去杜绝,甚至各个环节去解决这个问题。我这里介绍一些相关经验。</p>    <p>按照我们的一个研发周期,去把它拆成几个环节,比如说 <strong>开发,测试,灰度,线上,还有线上的监控</strong> 。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/ca372d6b6f9b408aa44c9e63b1a4e0bb.jpg"></p>    <p>我们把以前线上Crash上报,再去分析下一个版本解决的方式。用形成闭环的方式去解决一些问题,比如说,在开发阶段有这些举措。 <strong> 第一,Code Review </strong> 。 <strong>第二,预置开关</strong> ,很多新功能上线的时候,你做一些hook,做一些前瞻性改动,可能不是特别的确定,这个时候需要开关,如果没有开关碰到一些状况,一定会出问题。 <strong>第三,静态扫描</strong> 。出现问题,我们不光靠人,是通过机器、工具,总结出来一些规则沉淀成代码,把风险点扫描出来,并且要及时通过邮件短信的方式进行报警,防止破窗效应。 <strong>第四个就是动态检测</strong> 。</p>    <p>第二个环节: <strong>测试</strong> 。一般出问题都是来自于变动,变动对于客户端来说很多时候就是服务端response,比如说接口定好了,反馈的字段都是协商好的,大家都拍着胸脯说不会再有变化了。但是,出现变化的时候,可能服务端接口变了,字段缺失,类型也变化了。这种问题怎么去避免呢?在测试阶段我们也研发了相应的工具,我们会模拟服务端各种接口,数据,测试客户端,让它在各种异常情况下也不会发生问题。</p>    <p>第三个环节: <strong>灰度</strong> ,这个非常重要。就算开发测试阶段再怎么测试,团队人手都是有限的,怎么样去把这些问题更多地暴露出来?我们有渠道包,有内部一个企业包去测试,去发现这些问题。</p>    <p>这个阶段发现问题以后,去做好修复以后再发布到 <strong>线上</strong> ,这样的话质量可以得到比较好的保证。在线上,我们也是比较早开始就有这个技术了。然后我们还研发专门解决启动问题的安全模式,在去年双11之前就已经开始做了。还有客服端一些调试工具,便于发现问题。另外就是问题反馈,线上用户及时反馈对于我们来说也是非常重要的环节,人多力量大,移动互联网每个环境都是不一样,用户的及时问题反馈也是非常重要的。</p>    <p><strong>监控</strong> 这个环节非常重要,我们负责稳定性的同学会对数据进行监控,配置实时报警。比如说看实时Crash,原来基准线可能是5次,当它发生到10次的时候,我们就会收到短信,甚至是其他方式的及时报警,如果是Crash次数上升,范围会扩大,更多同学会收到报警,我们通过这种监控方式去及时发现问题。发现问题以后通过一些开关,包括修复手段把问题解决掉。但是,就算你考虑的再多方面,难免会出现一些故障。碰到故障以后,我们会进行故障的Review,把这一类问题甚至把这个故障为什么发生所暴露出来的各个环节去认真地讨论清楚。讨论了以后,不是为了定谁的责任,而是让大家明白问题在哪里,下一次如何去避免?我们是通过这些故障的Review,把这整个环节里面的措施一点一点补齐的。还有一部分就是Crash的定位,经验沉淀。定位Crash工具有很多,包括上报,分析等。负责稳定性的同学会把潜在一些风险点注意到,保证下一次不发生。通过这种方式,我们对于稳定性形成了一个闭环,下面对于其中一些环节重点做介绍。</p>    <p>开发阶段,需要重点介绍一下。动态检测,非主线程UI代码可以及时在开发阶段发现问题。静态扫描,凡是编译器报出来的OOM,我们一定必须去解决的。报警比较多,我们花时间把它梳理一下非常重要。防微杜渐,有一个效应叫做破窗效应,一个窗户破了,过一周看一下,其他更多地方都破了。所以,一定把任何的细节问题在开发阶段都杜绝掉,不要留隐患。</p>    <p>接下来就是配置中心。天猫客户端有非常重要的基础设施,我们会按照客户端类型,每一个版本都有配置。开发的时候,潜在有一些风险要上线了,我们都给它加上一个配置,开发人员自己形成一个习惯了。因为我们经常做这样的技术分享,加一个配置非常简单,首先写一个判断就可以了。最后在服务端,在后台我们把相应的配置加上去。</p>    <p>第一个就是Code Review。为什么要做Code Review?首先第一个问题在开发阶段解决是成本最低的,而且效率最高的。用Code Review不只是为了稳定性,还可以保障我们的代码可维持,架构也会得到不断的梳理和完善,第三个是我们能够对整体一个概况产生更直接的了解,还有可能我们有时候写代码会觉得,有别人看就可能写的优雅一点,让别人欣赏我们的代码。第五点,保证团队当中至少两个同学懂这一块代码,这一点非常重要。我分享一个案例,一个国外做游戏视频直播的公司,他们团队3个技术同学,每一个同学负责非常大一块功能开发。恰好团队当中有一个同学要出去旅游,美国人都比较潇洒,报一下老板也会批,休假了之后这个时候恰好碰上一个网红,在网站上直播,服务器挂了。只有那个同学懂,这个时候怎么办?最后问题很简单,那个同学重启机器就解决了。</p>    <p>所以,我想举的例子就是这样。尤其是团队和产品经理,这一点非常重要,团队同事很重要。团队不要出现单点,一定要有备份。这样的话,团队同学包括TEL,大家都是可以为整个的客户端的稳定性做出很好的保障。</p>    <p>最后一点,Phabricator。就是团队肯定有很多的安卓工程师,有很多的专家,所谓专家就是把所有错误都是犯过一次,或者看别人犯过一次的人。怎么样把这种经验传递下去?分享肯定就是要做的,但是那个频率不会特别高。如果让新同学把之前犯过的错误,把一些功能问题上去了再改这个成本非常高。通过Code Review就可以使得新的工程师经验得到快速成长。有的时候,相互做Review的时候,被Review的同学也是可以提一些很有趣的观点给一些资深的同学。所以,这个东西是使得团队的成员每一个人的经验,能力都可以得到提升。</p>    <p>这里有一个数据,大家可以看一下。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/a1fca6f629fbec0f562b08c26a44fcf4.jpg"></p>    <p>Code Review不是说写了代码我们去审查,而是在设计方案的时候同时在审查方案可不可行。大家可以看一下。通过Design and code inspections可以提前发现60%的问题。我们花很大力量去做单测只能解决20%,但是在前面做这些事情,成本更低,效果更好。Code Review在工业界的做法,硅谷,非死book,还有谷歌都是非常严格的去执行的。</p>    <p>现在我们团队已经有一半同学会去做提交前的Review,还有一些同学就是做提交后的Review,接下来就是推动让所有同学都做到提交前的一个Review。</p>    <p>说下我们的线上运维。我们把一个事件处理定义成这样几个阶段。 <strong> 第一监控,第二报警,第三解决,第四预防 </strong> 。第一,我们会有一个专门的平台,整个平台共用一套基础设施,可以把用户的Crash日志,包括Crash的数量、用户数,Crash率都在这个平台上面做处理。有问题的时候超过一定阈值,通过集团内部一个工具,叫做Xflush,它的目的就是为了监控报警。第三个阶段,就是修复它了,修复的手段就是刚刚讲的,第一就是修复工具,比如说iOS、安卓的一些修复手段。第二就是安全模式,一会儿介绍一下启动阶段的安全模式设计。第三个就是配置中心。第四个预防,通过这样一套体系来做线上的运维。</p>    <p>下面介绍一下天猫客户端安全模式,我们的1.0就是去年10月份做的,当时是重大活动一个前夕,压力很大。但我们还是在比较短的时间做出了1.0版本,就是用来解决启动的问题,当时设计两级安全模式,第一级安全模式的原理,我这边介绍一下。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/35f0255a381aa02e6ab25fd29a9f0637.jpg"></p>    <p>这个思路还是比较的简单,短时间连续Crash的时候,第一个动作是进入第一级安全模式,就是把关键点清空。从安卓市场下载APP第一次启动没有问题,有问题肯定也不会发出去。导致问题的一般来说都是服务端下发数据,第一步清除关键目录缓存,我们会让它正常启动。这个时候如果还是发生Crash,我们就让它进入到二级安全模式。让整个APP恢复到初始安装状态,把所有的包括非关键目录的其他地方的文件都清空掉。同时支持启动过程当中做热修复,这一点很重要。就是缓存搞不定的时候,我们可以通过Crash、热修复解决这个问题。</p>    <p>V2.0时做了更多的工作,比如说,安全模式在比较极端情况下,如果说你依赖很多SDK,我们做了一个解耦,把对于其他的SDK依赖全部解耦掉了。并且1.0的时候还有一个界面就是提示用户点此修复,我们再去修复这个问题。但是在2.0时开始做了一个无感修复。用户看不到这个页面,我们在后台默默把这些问题解决掉。因为在这种情况下快速解决问题才是最重要的。</p>    <p>然后,3.0版本我们做了SDK化。之后的4.0我们支持了灰度,并且支持测试。还有一个支持业务定制。举个场景讲一讲支持业务定制什么意思?假设APP不是启动的Crash,启动之后,点了首页一个按钮或者一个频道,再到频道里面才会加载一个缓存,但是,这可能就不算启动Crash,因为它是普遍过一段时间,这种问题怎么处理呢?我们的一个设计方案支持在启动阶段对于特定业务一个缓存做清除操作,在服务端增加目录地址,这个下一次启动的时候,不点有问题的地方,就是在启动阶段把有问题的干掉了。</p>    <p>下面这个图画的比较抽象一点。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/58c90e50d02bb22146b3fdb35edf242b.jpg"></p>    <p>配置中心刚刚提到了,数据中心就是一些补丁之类的。测试,我们之前说安全模式很重要,所以做了以后就是会想,如果承诺做了安全模式,但是过了半年,安全模式不生效,我觉得这个是有问题的。一开始测试时间比较久,就是制造各种各样的边界条件,开始测断断续续需要4个小时。我们根据很多的场景做很多内部小工具,把这一块工作简化掉,现在这一块的测试完成只需要10分钟就可以搞定了,大幅度的提升测试效率。</p>    <p>大家觉得一周之中线上故障周一到周日哪一天故障问题数最少?我讲一下答案。可以看一下这个图。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/b4d0c1f99ecd0b209ffef582ec42cc8f.jpg"></p>    <p>经统计发现,周六线上故障出问题的次数最少。为什么?因为周六改动最少。所以,用一个词,叫做no zuo no die,去改动很容易带来一些问题。一个改动会带来问题,下面的图是按照周维度讲的。为什么有两周完全没有故障;第一周就是12月最后一周,还有就是第二月第三周,这里直接讲答案了。第一个是圣诞节的时候没有问题,第二,是在帮同事做360度的绩效评估,这个就没有什么问题。所以,有一本书讲的好,可以给大家推荐一下:《你的灯亮着吗?》想改动的话,如果没有想出三个点,说明对这个问题思考还不是很深刻。不可以说我这个完全没有问题,但是可能还是不错的。所以,我们制订一个规则,就是在工作日期发布。这一点很重要。这个跟之前一些认知有一些变化。客户端的话,我们要在工作日期发布,这样就可以及时做回稳和处理,及时发现和解决问题。如果是周末可能找不到人。</p>    <p>灰度发布,这里是讲配置的灰度发布。经过分析发现改线上配置,是导致出现Crash问题一个非常重大的人为原因之一,如果有配置中心,改一个配置全量生效的话,非常容易导致Crash。所以从经验上来讲,我们配置一定是灰度发布的。我们在业务快速上线,快速迭代的同时,iOS、安卓的Crash率做到0.03%左右。这就是一个稳定性优化的结果。下一个阶段的重点,就是要解决Native Crash,包括iOS和安卓,还有OOM。</p>    <h2><strong>性能优化</strong></h2>    <p>简单介绍一下新的优化举措</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/0771260d00ff42b310abc95d8d4a14f8.jpg"></p>    <p>Webview大家可以看一下,刚刚讲过了,第一个就是WKWebview,第二个跟UC合作,安卓端用UC内核来做一个提升,用了UCWebview之后,速度提升了百分之十几。还有就是图片库做了很多优化措施,尤其是移动端,在4G、无线网情况下,对于图片的尺寸,屏幕大小,要根据屏幕大小做很多的裁剪、优化。网络这一部分做很多的比如说请求合并,包括使用缓存,还有SPDY,我们现在是支持SPDY的。关于流畅度,我们根据不同页面的情况对卡顿的地方做优化,把BS这一块提升上去。启动优化我们做了很多,比如任务分级,加载等等。最后一个是安装包瘦身,瘦身了之后,有一个业务会上去,这个是循环变化的过程。降下去了以后,过一段时间又上去了,这个是持续做的。</p>    <p>这里对稳定性和性能优化措施做一个总结。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/814bff1e28854b6cb0155f1bdfd156ce.jpg"></p>    <p>我们从救火式方式到形成闭环体系化方式去解决问题。就是用这样几个词语,预防,监控,解决,报警。每一个环节都是可以梳理做很多的自动化测试工具和经验沉淀。中间有一句话: <strong>Move fast with stable infrastructure</strong> 。我们要在快速迭代的同时保证基础架构的稳定。</p>    <p> </p>    <p style="text-align:center"> </p>    <p> </p>    <p> </p>    <p>来自:https://blog.tingyun.com/web/article/detail/1191</p>    <p> </p>