查看原文
其他

有赞移动性能监控平台(二)

有赞技术 有赞coder 2022-09-23



点击关注“有赞coder”

获取更多技术干货哦~

作者:陈明义
部门:同城零售

一、前言

性能和稳定性一直是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的稳定运行提供更好的保障,做好性能和稳定性的守门员。

拓展阅读:

  1. 有赞移动性能监控平台(一)
  2. 有赞移动 iOS 组件化(模块化)架构设计实践
  3. 有赞零售移动端收银商品实践
  4. 聊聊UI标准化
  5. 有赞零售 App 离线切换技术方案
  6. 有赞 Android 编译进阶之路——全量编译提效方案
  7. 有赞iOS精准测试实践
  8. 有赞 Android 编译进阶之路 —— 增量编译提效方案Savitar
  9. 有赞iOS-基于二进制的编译提效策略


Vol.376



您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存