干货 | 携程无线APM升级实践

2020-02-19 12:20:04 浏览数 (1)

作者简介

辛贵,携程无线研发总监。主要负责App基础框架研发相关工作,关注App开发框架、性能、质量、效率和新技术。

1、背景介绍

APM全称为Application Performance Management,即应用性能管理,对于一款成熟的App,各项性能指标的监控是必不可少的。公司内部早前有个1.0版本的APM系统,主要存以下问题:筛选功能薄弱、缺少日报和告警功能、功能混杂、且只支持单一App。鉴于以上问题,我们发起了APM2.0版本的重构,在开始之前,我们重新梳理了APM的定位,以及该做哪些功能。

最终,确定APM定位为数据报表 性能日报 监控告警 排障入口,具体如下:

  • 数据报表
    • 端到端精准数据报表
    • 多维度筛选功能支持
    • 多App报表支持
    • 实时报表
  • 性能日报
    • 核心性能指标提供邮件日报功能
    • 支持订阅功能
  • 监控告警
    • 适合告警的核心指标,进行告警
    • Crash率,JSError等告警
  • 排障入口
    • 支持多维度的异常数据、错误数据采样
    • 采样数据和内部系统打通

功能模块上,主要包括网络性能、页面性能、崩溃卡顿和专项性能四部分,下面章节会继续介绍。

2、主要功能

2.1 网络性能

网络请求性能是App的核心性能指标之一,APM平台以端到端的数据为基准,进行性能统计,这样最能反映用户的真实感受。

2.1.1 网络架构模型

如下图所示,App中的动态网络请求,都是通过自研的网络通讯框架发送到后端Gateway,Gateway再将服务转发给真实业务服务器。

几点简单说明:

  • 网络通讯框架和Gateway之间会通过TCP长连接进行通讯;
  • 网络通讯框架和Gateway之间数据协议为自定义契约格式协议;
  • 网络通讯框架可以将HTTP协议转换成自定义协议进行代理转发;
  • Gateway本身业务逻辑较少,只负责链路管理和请求路由转发,为了让用户有好的体验,我们做了全球部署;
  • 所有动态请求都通过网络框架发送,在这个点做好采样埋点,即可为APM报表提供精准数据源;
2.1.2 错误监控维度

端到端的网络请求异常数据,大多数是发生在链路的建立和数据传输上。由于我们是自己管理的TCP链路,因此对请求链路的可控性更加强,我们梳理了整个请求链路,对整个链路上存在异常的点,都定义了对应的code。参考下图:

主要以下code:

Code

定义

说明

-202

请求序列化失败

极小概率出现

-203

无可用链路

比例较高,无法连接到服务器,即为常见的网络不稳定

-204

发送请求失败

socket send失败,极小概率

-205

读取响应出错

链路异常,读不到指定长度的响应头

-206

响应反序列化失败

极小概率出现

-212

链路异常断开

包括客户端网络异常和后端主动断开

-213

读取不到响应

比例较高,即为常见的超时

以上错误code,主要是聚焦在自建TCP链路层面的异常,对于标准的HTTP Error,比如HTTP的4xx,5xx也会记录,一般出现这些错误的时候链路本身并不会出现错误(限TCP通道)。

2.1.3 监控的维度与目标

主要监控端到端请求的成功率和耗时两个数据维度。

  • 请求成功率
    • 计算方式:请求成功次数/(请求成功次数 请求失败次数)
    • 目标 - 用户交互场景:目标99%
    • 目标 - 整体平均:目标98% ,包含启动、后台场景的请求,这部分请求成功率相对较低
  • 请求耗时
    • 计算方式:客户端发起请求为起始点,收到响应并序列化完成为结束点
    • 目标 - 用户交互场景:后端处理耗时 300ms(RTT时间)
2.1.4 APM报表

下面几张截图是网络性能模块设计的报表:

上图是分版本,业务部门的性能概览数据,蓝色的字体,都是可以点击进行过滤筛选的。

上图是按照具体服务号,列出该服务的成功率、总耗时、服务端处理耗时、样本量、还有错误code分布。

每个表格后面都有一个采样功能,点击该按钮,即可进行采样,选择一批错误或者是耗时较长的设备ID,点击设备ID,可以跳转到内部排障系统,查看更多详细信息,进行整个问题的排查。

上图是内部排障系统中的一个小工具,可以根据一条链路ID来把该链路上的所有请求有序排列展示, 这对于日常排障非常有帮助,可以极大的提高网络问题的排障效率。

2.1.5 性能优化实践

以下是我们在进行端到端网络性能优化过程中的一些实践经验,简单介绍几个效果较好的优化方案。

  • 自定义通讯协议 使用自定义通讯协议,自管理链路,可以对请求中的每一个环节都做到完整的控制,对于故障排查和后续的性能优化会更加有价值。
  • 合理选择ServerIP 实际上包含两个场景的serverIP选择,一个是App刚启动时候默认使用的ServerIP,另一种是Server端下发适合的ServerIP之后的使用策略;几点经验, 国内场景,同一运营商效果较好,海外场景,服务器在海外部署,比通过加速通道回到国内源站效果要好,启动场景,根据timezone选择ServerIP比较合适;
  • 合理设置超时时间 超时时间设置过小,比如3s,成功率会比设置成15s的明显要低,所以可以根据服务的时效性,合理设置超时时间;
  • 支持重试 符合幂等性的服务,设置可以重试,可以大幅度提升成功率;

2.2 页面性能

2.2.1 页面性能统计方案
页面性能是关系到用户替换最重要的性能指标,如何去度量一个页面的性能,是没有一个通用的标准的。

一般在收到统计页面性能需求的时候,开发人员最常规的做法是在页面初始化的时候,设置一个时间点,然后在渲染所需的一个(组)服务发送完,页面渲染之后,设置一个结束点,两者相减,就是页面的可交互时间。

这种做法的统计是准确的,但是需要每个页面都去做这个逻辑,对于一些复杂页面,对于不同的业务逻辑,需要做多套这样的性能埋点处理。且后续随着业务的迭代,性能统计的逻辑还需要确保没有问题。

对于业务开发来说,这无疑是大大增加了工作量,我们希望能有一个框架层的方案,去统计页面渲染完成或者是可交互时间点(Time To Interactive)的性能,以便让业务开发人员从性能埋点中解放出来。

首先考虑到了页面截屏 像素点检测的方案去检测页面渲染完成时间点,思路如下:

  • 页面截屏,按照一定比例,去掉头部 底部部分
  • 将中间部分分隔成6块
  • 每个小块随机取点,检测像素点的相似度

按照这个思路实现并灰度上线了这套检测方案,基本准确的检测出页面渲染时间,但是也存在一些问题:

  • 性能损耗严重,每次截屏时间100ms
  • 对于一些骨架屏类的页面,会被当做页面渲染完成
  • 因为是随机取像素点,对于简单页面,页面检测的次数有一定的随机性

由于以上问题,这个数据上线之后,没能推广给业务开发同事去使用。继续优化,我们发现:

  • 页面渲染完成的时候,一般都有文本
  • 文本扫描的性能损耗比页面截屏低

在这两个观点支撑下,我们对检测方案做起了重构,将页面截屏 像素点的随机选取,替换成页面组件扫描 文案检查。大致流程如下:

  • 页面初始化开始,遍历页面所有元素,检测text
  • 如果text所在坐标在页面头部(20%)/尾部(25%)区域,忽略 Text >= 2组, 认为检测成功
  • 一轮检测之后,未检测成功,等待50ms进行下一轮检测
  • 总共检测10s,否则超时

以下视频是我们检测的demo,三个页面分别不同的技术栈,Native/CRN/H5, 可以看到,基本都是在内容加载完成的时候,toast弹出来提示页面检测完成。

该方案虽无法确认页面内元素是否完全渲染完成,但可以确定页面已经有内容,用户可以进行交互。所以我们将文案检测成功的时间点当做页面可交互的时间点,以此来作为页面的TTI(Time To Interactive, 后文使用TTI缩写)性能。

2.2.2 页面性能报表

上线之后,我们看到APM报表的数据,能比较真实的反映页面性能。

下图是APM平台上的页面TTI性能报表,支持多维度的筛选过滤,也支持采样功能。

对于每一个(组)页面,还能显示页面的TTI耗时分布,这对业务BU进行性能优化非常有帮助,简单相加就可以计算出页面TTI性能的95线,90线耗时。

在TTI性能表格的第一列,有一个页面类型,我们给每一个页面一个类型,每种类型设置一个TTI性能基准,TTI性能如果在基准之内的,显示绿色,超过基准20%的,显示红色,表示需要优化。以下是我们定义的基准,业务部门研发同事的页面性能优化以此为标准。

2.2.3 页面TTI性能优化

在配合业务团队研发同事进行页面TTI性能优化的过程中,积累了一些经验和优化方案。

  • 主线程耗时任务异步化
    • 将一些耗时,且在主线程操作的任务,调度到后台线程异步执行,可提升页面加载性能
  • 网络请求prefetch可大幅度降低页面TTI时间
    • 网络请求预取,即在页面跳转之前,将下一个页面需要的数据,预先获取回来,进入页面时候,只需要使用已经获取到的缓存数据
    • 可以大幅度提升页面性能,机票,酒店列表页面采用之后,页面TTI性能够有约40%左右的提升;
  • 预执行下一页面必须执行的任务
    • 对下一个页面必须执行的任务,在当前页面提前执行掉
    • 在下一个页面会下载离线包,优化为在当前页面下载下一页面可能使用的离线包;
  • CRN框架层的一些优化
    • 如果有使用ReactNative框架的话,强烈建议升级到0.61及其以上版本,并在Android平台开启hermes引擎;
    • RN提供的接口,大多是异步的,但异步接口在首屏加载,通讯频繁的场景,耗时不可控,可以换成同步API;
  • PreRender
    • 简单来说就是延迟页面跳转,利用延迟的时间进行页面的加载;
    • 只要延迟时间控制的好,对用户感知来说,就是页面在平滑的切换;

2.3 崩溃卡顿

崩溃卡顿系统,和大家常用的崩溃采集系统基本一致,这里不过多介绍。经常有用户投诉说遇到了闪退,但工程师在后台Crash收集系统里面却搜索不到对应的Crash日志。

技术的角度来说,是可以理解的,因为没有哪个Crash收集 SDK能够捕获所有的Crash,即便是操作系统也有一些不能捕获的,在iOS上也经常遇到App Crash之后在系统的设置里面,找不到对应的Log。

2.3.1 用户行为Crash

为了辅助统计用户遇到Crash的情况,我们在App启动的时候,去检测用户上次使用过程中是否有遇到Crash,如果有则上报上来,再开发成如下的用户行为Crash报表:

判断用户是否有Crash策略大致如下:

  • 启动App或者页面切换时候,持久化页面信息;
  • App被切换到后台时候,清空掉持久化的页面信息;
  • 启动App时候,检查上次的页面信息,如果上次页面启动时间和当前时间相差较短,则认为出现了Crash;

以上方案不能完全准确的统计用户是否遇到Crash,有些Case无法捕获,但多个版本纵向对比还是有意义的, 且随机采样几个用户行为进行分析,发现确实有Crash出现;

2.3.2 自定义异常上报

在开发过程中,经常会有各种Exception需要处理。大多时候开发人员直接将exception print出来,不做其他处理,也有部分Exception的处理需要单独埋点,后续再开发报表,这样做是比较严谨的,但是工作量偏高。

为了解决这些问题,我们提供了logException的API,以及如下的报表,方便开发人员直接上报Exception。上报之后,APM的报表会按照title(图中的Category)和subTitle(图中的Message)两级的方式组织报表,并提供采样功能,方便问题的排查分析。

3. 总结

本文主要介绍了我们为了监控App的网络性能、页面性能和稳定性这三个核心性能指标而开发的APM系统,实践下来,发现APM系统在以下几个方面对比较有价值:

  • 让我们对线上App的实际运行的性能数据有了更加清晰直观的了解;
  • 各项核心指标的监控和性能日报对线上App的稳定运营提供保障;
  • 为我们各项性能优化提供了标准和数据支撑,按照标准去优化,优化之后有报表数据支撑;
  • 推动业务开发团队进行性能优化,让业务开发团队更加重视自己的产品性能质量;

本文介绍的只是APM系统的核心部分,实际上还有大量定制功能无法一一展开,比如告警和性能日报、自定义报表、各类专项性能等。希望我们在网络性能、页面性能和稳定性方面的实践经验对大家有所启发。

注:“携程技术”微信公众号后台回复“apm”,可下载讲师分享PPT。

0 人点赞