有赞移动性能监控平台(二)
发布于 3 年前 作者 zengyong 4406 次浏览 来自 分享

一、前言

性能和稳定性一直是App质量体系中最基本和最关键的一环。 而移动端业务快速迭代的过程中,开发同学对性能的关注不足,量变引起质变,App的卡顿严重影响商家的日常经营,商家对性能的吐槽和抱怨越来越严重。而面对商家反馈的性能问题,由于缺乏现场信息,问题的排查既被动又困难。我们急需一个系统化的解决方案,需要有自己的APM(Application Performance Management),可以通过它来发现问题,并且能依靠它的数据来指导我们进行性能优化。

二、整体设计

统主要分为两个部分:

  • 移动端上的「性能检测」,主要负责数据的采集。
  • 后端的「数据处理」,主要包含数据的清洗、解析、存储、报警等。

2.1 性能检测

2.1.1 慢方法&ANR检测

有赞零售的业务复杂度非常高,且由于业务场景的特殊性,有大量的复杂业务逻辑处理都是在移动端上做的,本地存在大量的DB操作、数据同步、复杂计算…,此外收银设备的配置和性能相比于手机有很大的差距,这些都对我们提出了很大的挑战。卡顿问题也是我们面临的主要难题之一,为了解决这个问题,我们首先要解决方法运行效率的问题,找出应用中执行效率不满足要求的方法,通过优化这些处理逻辑,提高方法运行效率,进而提升整体的性能。

总体流程分为四步:

Step1:编译期自动采集需要性能检测的方法。过滤不需要进行检测的方法或类,为每个方法分配一个独立ID(包括二方库及三方库的方法),并生成方法ID与方法签名的映射文件。

Step2:编译期对方法进行插桩。通过插桩的方式对所有需要检测的方法进行插桩,在每个函数的函数体前后会自动插入i和o方法,入参为方法ID,这样就可以很方便的对执行时间进行检测。

Step3:运行期进行性能检测。比如慢方法和ANR的检测是通过记录i和o方法的时间并进行存储,会记录每个方法进入和离开的时间,当执行时间超出我们设定的阈值时,会将方法的信息进行上报,方便后面对数据进行处理。

Step4:收集卡顿现场信息。监听每一帧的变化,卡顿发生时,会有单独的线程去收集堆栈信息以及现场环境信息(包含:设备信息、CPU使用情况、内存、磁盘信息等)。

2.1.2 线程池检测

在Android开发中我们通常会将一些耗时任务放到子线程中进行操作,否则可能会阻塞UI线程,引起ANR、卡顿等问题。而我们的App中就存在大量的异步任务,包括网络请求、本地大量的DB操作、定时轮询等任务,而所有的这些异步任务都在同一个线程池中运行(IO线程池),这样就带来了一个问题:当网络请求比较慢的时候,线程池会快速堆积任务,再加上不断轮询的任务,线程池极易被打满,本地快速的一些异常任务就容易被阻塞住,造成耗时短的任务等待耗时长的任务。

为了解决这个问题,我们需要对线程池进行更细粒度的划分,考虑到我们应用的特性,我们对主线程池的定位改为:为大量、快速的本地任务提供支持,这样我们就需要将一些「耗时长」、「频率高」的任务进行治理,将这些任务分离出我们的主线程池,我们需要监控线程池中的任务,能通过监控的数据挖掘异步任务的优化点,同时为线程池的配置调整提供可度量的指标。

检测的核心在于监听线程池的三个节点:

  • 任务提交到线程池。拉取每个任务的调用栈信息,解析并记录任务的发起点以及任务的提交时间。同时可以分析此时线程池的负载状况(结合activeCount、corePoolSize、queueSize),如果负载达到阈值,则拉取出当前所有的任务,分析是否存在明显有问题的任务(比如同一任务发起多次等)。
  • 任务开始执行。记录任务开始执行的时间。
  • 任务执行完成。与任务开始时间做差值,计算并记录任务的执行时间,如果任务的执行时间超出阈值,则会进行上报。

总体流程如下:

通过上面的方式能逐步分离出「耗时长」、「频率高」的任务,通过对这些任务的优化,最终达到线程池优化的目标。另外由于有了完整的线程池负载情况跟踪以及任务执行时间数据,我们对线程池的健康度也有了可度量的指标,这也为我们做线程池的自动化配置提供了参考依据。

2.2 数据处理

2.2.1 整体流程

(1)方法映射文件记录。应用打包时会自动执行上面的编译期处理流程,生成方法映射文件,并上传到APM Server,然后进行方法映射数据的处理。

(2)性能数据同步。卡顿发生时,会上报相关数据。数据会通过DP(数据平台)进行清洗、计算和统计(比如前一天性能报告统计),然后通过Hive -> DB流程同步到后端应用的DB中。

(3)数据解析。后端应用会对数据中的卡顿堆栈进行解析(把ID解析成方法名)。

(4)数据聚合。解析完后需要对同一方法的数据进行聚合(包含跨版本、跨补丁的数据处理,比如A方法可能在1.0版本ID是11,在1.0补丁1版本的ID是22,在1.1版本的ID是33,需要把他们聚合成一条数据),这样就能准确的知道每个方法的严重程度,每条聚合数据也会记录对应的解决状态。

(5)数据分析&报警。会结合数据平台上计算和统计的结果以及数据聚合后产生的数据,产出日报、周报、告警等信息,为性能变化趋势提供数据化支撑。

通过上面的方式将同一个方法的问题进行聚合,可以跟踪所有问题的解决状态,并能通过数据分析进行性能变化趋势的监控以及问题的报警。

2.2.2 关键节点

(1)数据解析&聚合

由于上传的卡顿信息中只会包含方法的ID,我们需要在后端对这些数据进行解析,将它解析成具体的方法名,然而由于不同版本、不同补丁生成的方法映射文件是不一样的,同一个方法在不同的版本对应的方法ID是不一样的,而我们需要将同一个方法引发的问题进行聚合,这样才能有效的跟踪每个问题的解决状态。而是否是同一个方法的认定标准为:完整类名+方法名,这样就能帮助方法的唯一性。

整体解析&聚合流程如下:

(2)数据清理流程

整个监控平台数据虽然经过重重过滤,但是本身的数据量还是非常大的,任由数据无节制的增长会很容易到达我们处理的瓶颈,且这庞大数据中大部分是无需长时间存储的,我们需要有数据的清理机制,通过清理机制来保证我们数据不至于增长过快:

  • 未分配的问题且最近一个月问题不再出现,则这个问题大概率以后不会再发生了,针对这种问题我们会直接清除问题的所有信息,不会再跟踪这个问题。
  • 已分配但未标记解决的问题且最近一个月问题不再出现,这种问题一般是问题已经修复但是没有及时修改状态,我们会把问题的状态修改为自动失效。
  • 状态流转已结束的问题(包括已处理、已忽略、自动失效的问题),我们会保留最新的一条明细数据进行存档,剩余的明细数据会全部清除。
  • 方法映射表中会存储过往所有版本的所有方法信息,然而其中绝大多数方法其实是没问题的(比如简单的a+b),所以这里我们会对最近一个月解析时没有访问的方法进行清除(解析时没访问的方法意味着这个方法没发生卡顿问题)。
  • 同一个版本可能会上传多次方法映射表信息,而真正发布的只会有一个,因此我们会去发版平台获取某个版本正在发布的信息,然后把没有正常发布的方法映射表数据删除。

整体清理流程如下:

三、功能介绍

3.1 问题数据

3.1.1 TOP 问题列表

3.1.2 问题详情

3.2 问题报警

3.3 性能报表

3.3.1 ANR变化趋势

3.3.2 慢方法变化趋势

3.3.3 FPS变化趋势

 3.3.4 线程池卡顿次数

四、未来规划

  • 完善性能监控体系,补足网络、I/O、线程池、磁盘等方面的监控,通过性能监控的数据指导性能优化。
  • 更多维度的数据筛选和过滤能力,能够更好的对指定设备或门店进行问题跟踪。
  • 更强的数据处理能力,能够更快速地处理更庞大的数据量。
  • 完善系统使用体验,如:报表呈现、问题分配机制、问题报警等。
  • 推广到公司其它的业务线,帮助解决端上性能监控问题。

五、结语

通过性能监控的实践以及后续的性能优化,帮助我们更好的解决了性能问题,总结下来它的核心价值在于:

  • 对App线上真实的性能情况有了数据化的指标,让我们对性能问题有了更清晰的感受。
  • 通过监控的数据指导我们进行性能优化,优化效果也能有数据化的支撑。
  • 通过监控的数据能让我们更快速的发现新引入的性能问题,防止性能不断劣化,为App的稳定运行提供保障。
  • 推动各业务团队进行性能优化,让大家能更加重视自己产品的性能问题。

性能监控与性能优化道阻且长,我们也会不断地在这方面进行更多的尝试和努力,为App的稳定运行提供更好的保障,做好性能和稳定性的守门员。

感兴趣的商家,可点击 → 免费试用有赞店铺~

回到顶部