写点什么

Android 网络性能监控方案

发布于: 2020 年 11 月 10 日
Android网络性能监控方案

阿里云 云原生应用研发平台 EMAS 刘宝文(木睿)

背景

移动互联网时代,移动端极大部分业务都需要通过 App 和 Server 之间的数据交互来实现,所以大部分 App 提供的业务功能都需要使用网络请求。如果因为网络请求慢或者请求失败,导致用户无法顺畅的使用业务功能,会对用户体验造成极大影响。


此外,EMAS 对外提供的 APM 之前并不包括网络监控功能,而网络性能监控作为移动端性能监控的重要组成部分,我们急需补全这部分能力来完善 APM 的产品功能,进一步满足客户的需求。


“阿里巴巴应用研发平台 EMAS 是国内领先的云原生应用研发平台(移动 App、H5 应用、小程序、Web 应用等),基于广泛的云原生技术(Backend as a Service、Serverless、DevOps、低代码等),致力于为企业、开发者提供一站式的应用研发管理服务,涵盖开发、测试、运维、运营等应用全生命周期。”


问题与挑战

网络性能监控在端上主要包括数据采集和数据上报。我们希望能尽可能采集有用的信息来帮助客户发现、定位和解决网络性能问题。我们面临如下问题和挑战:

  • 首先要解决的是网络请求过程中,哪些阶段会影响请求性能,如果发现网络性能有问题,需要采集哪些数据来帮助用户去定位和解决问题。

  • android 上主流的网络框架有 okhttp2、okhttp3、okhttp4、volley、retrofit、httpclient 和系统提供的 httpurlconnection 等,在我们不确定客户使用哪个网络库的哪个版本的情况下,如何尽量采集有用的信息。

  • 网络请求各个阶段的数据采集都是离散的,如何保证单个请求各个离散的监控数据能够串联起来,不和其他请求的监控数据混在一起。

  • 由于弱网环境下的网络请求日志往往更有价值,需要尽可能将异常的网络请求日志数据上报到服务端。

  • 并发网络请求时,需要确保在日志上传时尽量不影响客户正常业务。


实现方案

网络性能监控在端上的具体实现主要包含两大模块:

  • 数据采集

  • 数据上报

其中数据采集是整个 SDK 框架的核心。


整体架构概览:


image.png


接入层:

网络监控属于高可用产品的一部分,采用高可用统一接入的方式接入。

插件层:

高可用目前框架是通过插件式的方式集成各个业务,实现 networkmonitor plugin 集成到 APM 中,补充 APM 中网络监控部分。

逻辑层:

主要负责采集控制、数据管理、缓存管理和数据上报。

拦截器层:

整个网络监控的核心。为了采集更多的信息,我们选择使用字节码注入技术来实现网络请求监控功能。对 OkHttp、HttpClient 和 HttpUrlConnection,分别实现 Interceptor 去采集不同网络库中网络请求各个阶段的数据,并在请求结束时完成采集进行上报。此外,通过自定义 gradle plugin 的方式,为各个网络库实现 Injector 和开关,控制在应用构建阶段将 Interceptor 中各个采集的方法注入到对应网络库字节码的埋点位置,从而实现在运行时网络请求各个阶段采集需要的数据。

数据采集

采集哪些数据

首先需要确定采集的数据范围来帮助我们及时发现网络请求的性能和异常等情况,另一方面也需要有额外的数据来辅助排查问题。所以我们采集的数据主要包括四个部分:

  • 基础数据。

  • 性能数据。

  • 异常信息。

  • 事件序列数据。

基础数据


image.png


  • 请求 url:对请求做聚合运算。

  • 目标 IP 地址:对于多出口 IP 的客户,支持 IP 地址维度的数据分析。

  • dns 解析结果:请求 url 的域名解析 ip 列表,用于分析是否存在域名劫持的问题。

  • http code:根据 http code 确定请求状态。

  • 上行流量:包括整个请求上行 header 和 body 的总的流量,包含重试和重定向的上行流量。用于监控上行流量开销。

  • 下行流量:包括整个请求下行 header 和 body 的总的流量,包含重试和重定向的下行流量。用于监控下行流量开销。

  • 网络库类型及版本:对于客户更换网络库或者升级网络库版本的情况,可以提供前后的网络数据的差异。

性能数据

性能数据主要是采集整个网络请求中各个阶段的耗时情况来定位慢请求发生的阶段。下图列举了 http 请求可能出现的各个阶段。


image.png


所以性能数据部分需要采集下述各个阶段的耗时数据:

  • 整个网络请求耗时

  • dns 耗时

  • 建连耗时

  • TLS 建连耗时

  • 数据上行耗时

  • header 上行耗时

  • body 上行耗时

  • 数据下行耗时

  • header 下行耗时

  • body 下行耗时

异常信息

异常信息主要是收集网络请求各阶段出现异常时的异常栈的信息。比如常见的 java.net.UnknownHostException、java.net.SocketTimeoutException 等。

事件序列数据

事件序列数据主要是收集网络请求各阶段的监控事件的信息,另外对于特定网络库的一些特殊的事件的监控,比如 okhttp 的连接复用、自动重定向和失败重试等对网络耗时有影响的机制。最后将这些事件按时间顺序排列。


比如在 okhttp 上 dns 被劫持的场景,我们通过基础数据中的目标 IP 地址去判断 dns 劫持情况,这个目标 IP 地址是在建立连接的时候去采集的。如果第一个请求发生了 dns 劫持的情况,那这个请求我们能正常识别的 dns 劫持已经发生。如果后续的网络请求复用了这个连接,因为不会再去建立连接,所以基础数据中没有目标 IP 地址,这时候就需要使用事件序列数据中的连接复用事件中的连接的 url 和目标 IP 地址来判断是不是被劫持的请求。

如何采集数据

字节码插桩原理

字节码插桩涉及到 Android 的打包构建流程。首先我们看下 Android 应用程序的打包流程,如下图:


image.png


从上图可知,我们只需要在 javac 之后 dex 之前遍历所有的字节码文件,并按照一定的规则过滤修改就可以实现字节码的插桩。


从 Android Gradle 1.5.0 开始,Google 官方提供了 Transform API。通过 Transform API,允许第三方以插件的形式,在 Android 应用程序打包成 dex 文件之前的编译过程中操作.class 文件。


image.png


Android 编译器中的 TaskManager 将每个 Transform 串起来,第一个 Transform 接收来自 javac 编译的结果,以及已经拉取到本地的第三方 sdk(jar、aar),还有 resource 资源。这些编译的中间产物,在 Transform 组成的链条上流动,每个 Transform 节点可以对 class 进行处理再传递给下一个 Transform。常见的混淆、Desugar 等的实现就是封装在一个个 Transform 中。而自定义的 Tranform 会插入到这个 Transform 链条的最前面,所以开启混淆的情况下通过自定义 Transform 对字节码进行修改也是先修改字节码再混淆。


网络库调研

除了系统自带的网络库 HttpUrlConnection,在 android 平台还有很多优秀的第三方网络库,大部分 App 开发会使用第三方的网络库来发起网络请求。


从上表中主流网络库的底层实现来看,我们只要支持 OkHttp、HttpUrlConnection 和 HttpClinet 的数据采集就能满足主流网络库的性能监控需求。


image.png


我们对应用市场上 Top1000 的 App 进行了分析,按集成数量排序依次是 okhttp3&okhttp4、volley(HttpUrlConnection)、okhttp2 和 httpclient。其中 okhttp 网络库占比将近 80%,所以我们优先实现了 okhttp 网络库的监控实现。


okhttp 网络库的监控实现

okhttp 网络库家族主要包括 okhttp2、okhttp3 和 okhttp4。其中 okhttp3 版本分布众多,底层实现变化也最多,而 okhttp2 的底层实现和 okhttp3 的早期版本相近,okhttp4 是 okhttp3 的 kotlin 版本的实现。所以我们主要介绍下 okhttp3 上的监控实现。


image.png


上图是 okhttp3.12.0 版本的实现框架,我们在网络库的具体逻辑里注入代码来采集需要的数据。

okhttp3 版本众多,从 3.0.0-3.14.9 已经有超过 40 个版本,对于每一个代码注入的位置都需要确保再各个版本上能正常工作。所以实现 okhttp3 的无痕埋点,版本适配需要耗费大量的工作。


数据上报

数据上报,除了需要考虑加密、鉴权、压缩等方面,还需要能确保尽可能少的丢失日志,同时还需要控制资源的占用来降低对上层业务的影响。具体实现主要包括两方面:

  • 缓存:支持内存缓存和磁盘缓存两级缓存。需要实现业务隔离,多个业务使用缓存功能时可以做到互不影响。

  • 上报:由于 APM 产生的日志较多,为了控制并发数和内存,我们使用了一个业务共享的线程池和调度队列。调度队列最多缓存 10 条批量日志,如果超出 10 条会立即将日志放入磁盘缓存。另外在上报前提供了日志预处理的开放接口方便业务层对日志做处理,比如抽样、聚合等功能。


后续计划

EMAS 网络性能监控已经对外开放,产品详情:https://www.aliyun.com/product/emascrash/apm,后续我们会根据客户实际需求去逐步完善功能。下一步计划实现的需求包括:

  • 支持 HttpUrlConnection、HttpClient 等网络库。

  • 支持 body 数据的采集上报,让客户可以感知、定位和解决在网络连通性正常,但服务端下发异常数据导致端上业务出现异常的问题。

  • 支持日志数据端上预聚合,降低服务端存储压力。

  • 支持 socket 请求的监控。


欢迎大家积极留言,提出你们的宝贵意见和建议,非常感谢!钉钉搜索 35248489,加入阿里云云原生应用研发平台 EMAS 技术交流群,探讨最新最热门的应用研发技术和实践。


发布于: 2020 年 11 月 10 日阅读数: 223
用户头像

云原生应用研发平台EMAS与小程序技术交流 2020.07.20 加入

移动研发平台(Enterprise Mobile Application Studio,简称EMAS),为企业、开发者提供一站式的应用研发管理服务,涵盖开发、测试、运维、运营等应用全生命周期。获得更多资讯,欢迎加入钉钉群:35248489

评论

发布
暂无评论
Android网络性能监控方案