别催更啦!手淘全链路性能优化下篇--容器极速之路

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 历时1年,上百万行代码!首次揭秘手淘全链路性能优化(上)我们重点介绍了手淘在性能优化中的一些实践和思路,主要集中在原生的代码的优化,这次,我们将继续分享在手淘容器化页面如 H5 及 Weex 相关的优化实践。

作者|手淘用户体验提升项目组

出品|阿里巴巴新零售淘系技术部


Weex 化的店铺性能优

▐  背景


店铺业务做为商家运营、营销的主阵地,是动态化方案激进的跟进者之一。2015年上线 Weapp 技术,就是在一码多端上的尝试。2017年,WeappPlus 的项目斐然崛起也和店铺有着千丝万缕的联系。


没看错,就是 Weex 的第一个项目名。之后 Weex 如一阵飓风般刮过集团各大业务,或整体、或单页、或卡片的应用场景层出不穷。店铺作为第一批上线 Weex 的业务,承担积极的推动作用,甚至一度把客户端给做没了,客户端仅保留了店铺路由的逻辑。在页面渲染和呈现上,全业务、全页面都由 Weex 来承载。


但是众所周知,Weex 技术在带来种种优点的同时,也有一个无法避免的短板。页面进入速度会变慢(相对于纯客户端页面而言),特别是店铺这种既需要复杂交互,同时还有三方 ISV 开放能力,单个页面的 js 超过 500K 的复杂业务场景。


在结构上,店铺的分为 框架 - 子页 两部分:


框架包含店铺名,心钻冠等级,顶部 tab 条,底部 tabbar 导航的能力。子页就是各个 tab 切换后的中间区域。

首页就是屏幕中央的主要业务区域,可以通过 tab 完成多个页面间的切换。

框架 和 子页,通过 embed 标签完成嵌套关系。

同时为了让用户获得更大的浏览区域,引入了 Nested-Scroll, BindingX 等能力实现嵌套滚动,底色渐变等能力。


最后店铺外投的 URL 形式众多,需要有能力支持各式各样的入口地址。


综上,整个进店流程从链路上说可以分为三个阶段:路由,框架,首页。

image.png


接下来,将从设计、分析、实现、上线等角度阐述整个优化工作的具体实现。

希望能够帮助读者在优化自身业务时,有更多的方法。


▐  开工


★  优化开始的第一件事是什么?


对,就是找把尺子。


先确定好衡量的环境 和 数据口径,做到手中有尺,心中不慌。


只有明确了标准,才能使工作的价值 更清晰、更准确、更容易 得被衡量。


特别当兴冲冲得找老板汇报已经完成目标,结果一测发现还差一大截时,更能体会这条 tips 的重要。


image.pngimage.gif

★  第二件 备好工具


直接代码打性能日志


(最简单也是最好用)


有个小建议,同时输出时间戳 和 时间差。同时按一条主要的关键路径,把先后的日志按顺序串起来。SystemClock.uptimeMillis() 不推荐使用,请使用 System.currentTimeMillis() 。


这样有个好处,当有多个系统交互,比如 前端 客户端java 客户端c++ 就在一条时间基线上,串起来看清晰很多。


image.png


Systrace + TraceView + top + Charles


Systrace: 目标是跑满 cpu,这个在项目优化初期做一下,对整个流程有个宏观感觉。项目冲刺阶段做一次,重点突破。


TraceView: 可多看看,找耗时方法很有效。


top: 在遇到分析瓶颈的时候,不妨试试,往往能看出一些线索,比如 USER CPU 占用偏高,重点分析是否和多线程场景 和 检查 c++ 任务调度。


Charles:在定位到底是 网络慢 还是 处理慢 的问题上,往往一针见血。


所有工具都是手段,最重要的还是对 流程的理解 和 业务的熟悉。


adb shell input tap


input tap 这个简单的特性,往往能化腐朽为神奇,减轻偶现问题的修复验证工作。


▐  技术选型


回忆下前面提到的店铺渲染三大阶段:整个优化工作都是围绕这三个阶段展开。这里进一步对三步进行细化,见下图。


image.pngimage.gif


★  框架 - 子页 并行渲染


在上述店铺三阶段中,可以发现 店铺框架 和 店铺首页,作为两个独立 WXSDKInstance,在整体流程中耗时占比较大。很朴素地就考虑到能不能将这两步并行起来。


优化前由前端代码通过 embed 标签,直接就能确定了「子页」和「框架」嵌套关系 和 嵌套位置。流程相对简单。


在引入并行渲染的优化后,事情变复杂。由客户端并行承载两个页面的绘制工作,再选择合适时机,把两个页面组合起来。


image.png

从流程上来,「框架」和「子页」的 weex 渲染任务会并行进行。并且同时取消了「子页」的 loading,在用户交互感知上少了一个等待的过程。体感上提示很明显。


image.png


★  数据请求托管


经过分析,传统方式下,一次店铺打开,共有五次数据请求。虽然前端可以利用 Promise 技术做并发,但是依然存在等待数据的状态。


针对上述五次请求,具体场景不同有不同的优化策略,其基本方向还是数据请求合并,并发请求,异步请求。


过程中的注意点:


1. 最优先考虑的还是数据合并,将多个数据请求合并成一个,最简单且效果最好。

2. 并行数据请求,复用的是 weex 实例创建的时间,越早请求,效果越好。

3. 异步请求的场景,远程数据获取 和 前端数据拉取 的先后关系不定,要注意数据缓存的设计。

4. 对于所有处理数据的weex 模块,所有方法都不需要主线程执行。


★  JS-Bundle 提前准备


总所周知,优化 JS-Bundle 的加载速度,是提升 weex 业务体验最有效和最直接的抓手。


针对绝大多数 weex 场景,通过手淘现有Cache 技术完成 JS-Bundle 的客户端侧存储,已经可以很好地支持业务。但对于店铺这个 pv 亿计体量的业务还略显不足。主要体现在:


  • 到达率无法准确把控
  • 更新时间较久
  • 磁盘加载稍慢。


针对上述几点改进空间,并且结合业务特点定制了 JS-Bundle 端侧存储能力。主要特点:


1. 除首次纯新安装场景,其余情况总是优先返回本地缓存的 JS-Bundle。同时根据两次访问的时间差,判断是否要刷新本地缓存。且遵循 http 的 cache-control 策略。

2. 客户端侧底层存储使用统一存储,在手淘打开后的首次访问时,读数据很慢,甚至在高端机上比走网络还慢。所以选取合适的时机做预热。

3. 内存级缓存。内部使用 SoftReference 包裹数据体,防止 OOM。同时在 切后台 和 接到低内存警告 的时候,触发对象释放机制。


在引入 JS-Bundle 提前准备后,用户最直观的感受就是整页白屏 loading 等待的时间大幅缩短,甚至页面转场完成时,首页就已经渲染完毕,避免了用户视觉上的空窗期


image.gifimage.png


★  上下游优化


再提几个比较容易忽略的优化点。


Weex Module 的调用


每次 module 调用虽然耗时很短,但是因为每次都要等待 WXJSBridge 线程空闲,并且获取到 cpu 时间片才能执行。所以常常会出现方法调用耗时 10ms,等待耗时 300ms 的现象。这点主要从前端优化。


一方面尽量减少 module 调用,特别是关系到主链路的渲染流程中的 module 调用。

另一方面,将不重要的 module 调用延后。例如店铺就将部分 ut 埋点逻辑,做了延后处理。


流程上看,通过减少 JS-bridge 的任务调用,减少线程切换时间,从而减少 weex 渲染 view 的上屏时间。最终减少用户可流畅交互时间。


image.png

图片预加载


针对图文较多的业务,在客户端获取到数据接口后,直接利用图片库的预请求能力,做图片的加载。


image.png

硬件加速


部分手机厂商向公司开发了 API,允许在特定场景下调升 CPU 频率,从而实现加速的效果。


最后也是最简单的一点


减少 JS-Bundle 的文本大小,是最直观的提效做法。


优化业务的前端代码,去掉过期的逻辑,去除无用判断,去掉冗余的依赖。


店铺业务大约减少了 80K+ 的 js,获得了大约 40ms- 的性能提升。


综上:所有优化点上线后。整个流程链路以下图流程经行。


image.pngimage.gif

通过店铺性能调试工具可以实时验证观察各阶段的渲染效果。


image.png

Web 前端性能优化实践


前端 Web 页面的性能给大多数人的印象可能还停留在几年前的加载白屏、滑动体验差等,但是到了现在这个时间节点,必须停下来重新思考下现有的业务发展、技术开发方式等,最终我们觉得有理由并且有能力从全链路的角度来看前端 Web 页面性能,最终突破这一刻板印象。


▐  优化思路


在进行优化前,梳理了几个原则:


1.明确目标:整体页面端内首屏时间 1.2s,端外首屏时间 3s,低端机首屏时间小于 2s,可交互时间小于 2.5s,可流畅交互时间小于 3s。性能优化无止尽,定好目标才能有的放矢。

2.梳理端到端系统化每个环节:梳理宏观的每个环节,再具体分析到细节点。

3.数据驱动性能优化:线上收集各个环节的性能数据,便于分析。

4.投入产出比 ROI: 不要迷失在性能优化中,思考技术对于业务的价值。


★  各环节耗时分布


image.pngimage.gif


这张表统计了从点击开始到渲染完成各个环节的耗时分布,各个最低数值代表理论可行值,最大值代表线上真实数据耗时,因此可以清楚的知道哪个环节现阶段是最差的,然后进行针对性的分析和优化。


最终会不断的调整这张表的数值分布,然后看哪个阶段还能继续优化,形成优化并数值调整 -> 线上验证 -> 再优化调整的正向循环。


▐  渲染方案


目前基于线上数据验证以及线下对比,如果一个页面资源(包括文档 HTML、js 资源)都是走缓存的形式,整体页面的性能、体验会比较好,对文档、资源的缓存非常关键。因此整体的渲染方案会最大限度的利用缓存,设计如下:


image.png


1.袋鼠服务:面向前端的数据接口合并服务,模块直接取数据渲染,保证了页面渲染的一致性

2.统一渲染页

  • 数据驱动页面渲染,所有页面的核心逻辑一致
  • 保证模块渲染与页面的分离,确保统一渲染页被稳定缓存

3.页面渲染策略:区别首屏/非首屏渲染,保证首屏能快速渲染

4.模块缓存池:解决所有页面模块缓存问题


▐  优化系列


基于性能优化的手段,目前我们可以主要分为以下三种:


★  加载


对于一张页面的加载,我们要做到 4 个 1:


  • 一个共享文档
  • 一个首屏关键 js
  • 一个首屏业务 combo 资源
  • 一个全局共享缓存池


主要罗列了以下几个优化手段:


1.按需加载:某些特别大的 js 资源可以按需加载,比如安全脚本大小会有 500K+

2.分环境加载:由于目前端上 es6 环境支持的程度已经较好,因此可以在绝大多数场景去除 babel-polyfill 的依赖

3.资源缓存:端上 combo 资源按版本拆分缓存


资源缓存


image.png

1.端上共享缓存池,缓存 combo 后的资源到内存以及缓存解 combo 后的缓存到内存/磁盘,这样在二次访问时达到重复利用的目的。

2.优化资源传递消耗,内核之间只需 Stream 流形式传递,避免了类型如 byte -> String -> byteStream 之间的类型转化消耗。


优势:

1.无需推送 ZCache,共享单个级别的资源

2.性能好:缓存 20-100ms vs网络请求 200-300ms+

3.优于 httpcache,可以命中更多的 combo 组合


劣势:

1.会占用一定的内存来换取资源读取上的优势


★  渲染


对于渲染,我们要做到首屏尽可能快的渲染完成,主要罗列了以下几个优化手段:


1.首屏/非首屏渲染,更细粒度的按行懒加载:对于一整张页面,我们希望能优先渲染首屏,可以尽快的呈现内容让用户看到。有时候首屏的内容也会非常长,还可以做到按行级别的控制懒加载渲染。

2.js bridge 通道优化:端内需要有较多的与 native api 交互,通信效率也至关重要。


js bridge 通道优化


image.png

核心:解决JS引擎与内核 Java 层同步 IPC 通信阻塞问题


优化前


使用 prompt 方法进行 jsbridge 通信


1.JS 引擎与内核 java 层同步IPC通信,阻塞 Render 线程,通信耗时:中/低端机100-200毫秒,导致延长渲染时间

2.引发内存泄漏问题

3.引起渲染引擎重排版,平均耗时 100ms 左右


优化后


使用 onConsoleMessage 方法进行 jsbridge 通信


1.JS 引擎与内核 java 层异步IPC通信,不阻塞 Render 线程

2.不引起渲染引擎重排版


★  数据


数据 Prefetch & 时机提前


核心:解决数据/渲染并行执行,尽可能的减少数据请求时间


image.gifimage.png


1.利用了容器的初始化时间,进行并行数据请求,从而节省数据的请求时间

2.进一步提前数据请求时间点到点击

最终的节省时间 = 容器路由时间(找哪个容器渲染) + 容器启动时间 + 容器初始化时间 + 页面框架渲染时间


中间过程优化


核心:解决子线程与 UI 线程的阻塞问题,以及中间环节没必要的类型转换


优化前


image.png


优化后


image.png


最后


从宏观到微观。性能优化之初一定要有宏观视角,对整个框架的运行情况,要有做到心里有数,对问题进行全面分析,然后再对瓶颈进行拆解,拆解后的子任务也不能孤立去看,一定要放在系统内,综合选择最优方法。


建立长期管控机制。性能优化成本也非常昂贵,性能优化在某种程度上,与其说是技术人员秀肌肉,不如说是还债,技术债(当然,真正有技术挑战在一些特定条件下做极致优化的情况不在此列)。从最初的技术方案设计,业务压力下充忙上线,线性的功能堆积,对现有架构设计的妥协等多种原因导致了性能问题的。如果在上线之初就能考虑到对性能的影响,好好设计方案,这时的成本是最低的。然而,一切依赖人的行为的机制都是不靠谱的,老虎也有打盹儿时。要减少运动式的做性能优化,需要建立一个依赖于客观数据长效的监控机制,这也是我们正在探索的方向。

路漫漫其修远兮,吾将上下而求索。

相关文章
|
12天前
|
存储 Kubernetes 调度
基于容器化技术的性能优化实践
基于容器化技术的性能优化实践
25 3
|
3月前
|
域名解析 Kubernetes 负载均衡
在K8S中,外部访问容器服务,比如说提供了一个域名,链路怎么走?数据经过哪些组件?
在K8S中,外部访问容器服务,比如说提供了一个域名,链路怎么走?数据经过哪些组件?
|
弹性计算 Kubernetes 调度
《Docker资源限制和调度策略:性能优化与资源管理,打造高效稳定的容器环境》
《Docker资源限制和调度策略:性能优化与资源管理,打造高效稳定的容器环境》
301 0
|
安全 Cloud Native 网络安全
容器DevSecOps全链路体验
本场景带您体验基于ACR EE的云原生应用交付链实践,如何将安全内置在DevOps的流程,将流水线升级为DevSecOps,保障端到端的容器业务安全。
|
域名解析 弹性计算 运维
带你读《企业级云原生白皮书项目实战》——3.3.1 入方向
带你读《企业级云原生白皮书项目实战》——3.3.1 入方向
133 0
带你读《企业级云原生白皮书项目实战》——3.3.1 入方向
|
弹性计算 监控 Cloud Native
带你读《企业级云原生白皮书项目实战》——3.3.2 出方向(上)
带你读《企业级云原生白皮书项目实战》——3.3.2 出方向(上)
|
弹性计算 Cloud Native Perl
带你读《企业级云原生白皮书项目实战》——3.3.2 出方向(下)
带你读《企业级云原生白皮书项目实战》——3.3.2 出方向(下)
104 0
|
物联网 开发者 容器
《workshop专场--容器、消息&IoT专场-开发者动手实践营-容器、消息和IoT--物联网端到端全链路开发》电子版地址
workshop专场--容器、消息&IoT专场-开发者动手实践营-容器、消息和IoT--物联网端到端全链路开发
125 0
《workshop专场--容器、消息&IoT专场-开发者动手实践营-容器、消息和IoT--物联网端到端全链路开发》电子版地址
|
Kubernetes 监控 Dubbo
k8s容器云架构之dubbo微服务—全链路监控安装
官方地址:https://github.com/pinpoint-apm/pinpoint-docker 下载这两个文件并修改多余的服务具体修改配置细节参考如下文档即可。
491 1
|
云安全 供应链 安全
[10.14 Workshop] 容器 DevSecOps 全链路体验
DevOps的价值已经在软件开发企业间形成了共识,尤其在云原生时代,容器化架构与 DevOps 结合在一起,极大提升了企业研发效率。然而在快速迭代的背后,如果没有一个可信的软件供应链,很可能将安全风险引入生产环境,造成巨大的损失。本文将基于 ACR EE 的云原生应用交付链实践,介绍如何将安全内置在 DevOps 的流程,将流水线升级为 DevSecOps ,保障端到端的容器业务安全。
560 0
[10.14 Workshop] 容器 DevSecOps 全链路体验
下一篇
无影云桌面