万里鹏—字节跳动线上性能监控体系的建设

2020-03-01 367浏览

  • 1.字节跳动线上性能监控体系的建设 万里鹏 性能监控体系负责人
  • 2.自我介绍
  • 3.自我介绍
  • 4.自我介绍 万里鹏 字节跳动Android基础技术监控体系 性能优化与监控体系,包括稳定性、异常、性能,事件等上报和监控
  • 5.目录 • 研发流程和现状 • 性能监控体系的建立 • 单点问题追查
  • 6.研发流程和现状
  • 7.研发现状和痛点 庞大的亿级用户群体 Android机型碎片化、性能差异较大 直接影响产品体验、核心指标 用户反馈描述不容易定位问题 本地测试,线上偶现异常
  • 8.研发流程中的问题 在研发流程的不同阶段中,用户量级的不同,导致不同概率的问题遗漏到下一阶段 研发阶段 更更多关注功能 QA测试 覆盖机型有限 内测版本 版本灰度 正式发布 ⽤用户量量级较⼩小 量量级和正式版有差距 最终全量量级发布 研发流程中,每个阶段均能发现不不同的问题,但是最终漏漏掉的问题,在线上造成不不可预知,情况复杂多变, 所以线上监控变得⾮非常重要
  • 9.问题发现 • 量量级少,暴暴露露 • 不不容易易复现 问题不不明显 内测 • 易易复现 ⽤用户反馈 • 难复现 • 量量级⼩小 • 量量级⼤大 RD/QA • 情况理理想 • 场景覆盖不不⾜足 线上监控 • 真实情况 • 需要健全的监 控体系
  • 10.线上监控流程 发版/热修复 定位问题,需要监控平台满⾜足 监控数据采集 • 覆盖更更全⾯面的性能类型 • 性能问题的归因能⼒力力 • 不不同性能监控的采集技术 定位问题 监控数据上报 • 监控功耗 • 多维度过滤性能问题 • 低内存占⽤用 • 交互更更加易易⽤用的监控平台 • 低CPU占⽤用 报警/主动发现 数据聚合分析
  • 11.性能监控体系的建⽴立
  • 12.监控体系
  • 13.性能监控体系 内存监控 卡顿指标 缓存监控 电量监控 异常流量
  • 14.内存监控 监控系统发生OOM、内存触顶时的内存分配
  • 15.线上内存的痛点 01 内存溢出 线上发⽣生OOM之后,堆栈并不不⼀一定是 真正造成内存溢出的原因 内存 问题 内存泄露露 OOM率逐渐增多,内存泄露露也是导致 恶化的原因之⼀一 03 02 内存上涨 观察⼤大盘数据,某个版本开始 突然内存逐渐上涨。很难快速 定位到具体原因
  • 16.内存监控功能点 内存溢出 OOM发⽣生时分析内存占⽤用 的详细⽐比例例,分析原因 内存触顶 内存使⽤用⽐比例例达到阈值, 触发内存占⽤用分析 OOM ⼤大对象 Object Warn 分析内存中⼤大对象的占⽐比 内存泄露露 Leak Activity和Fragment的内 存泄露露检测
  • 17.内存分析流程 触发 上报 dump内存镜像 • 镜像⽂文件较⼤大 • 与当时内存⼤大⼩小 有直接关系 • dump耗时较多 剪裁Hprof⽂文件 • 针对⽂文件格式,剪 裁掉⽆无⽤用部分 分析镜像 • 独⽴立进程分析 • 优化分析内存占⽤用 • 间接优化分析速度 • 内存泄露露 • ⽐比Hook⽅方式更更安全 • ⼤大内存对象
  • 18.卡顿指标的建⽴立 基于FPS,建立卡顿指标,配合慢函数解决卡顿问题
  • 19.FPS的感知度弊端 场景⼀一:平均滑动帧率57,期间出现2次丢帧 20帧(320ms) 10帧(160ms) 快速滑动过程中突然停顿320ms,⼜又继续滑动, 再次卡顿160ms 场景⼆二:平均滑动帧率56,期间出现多次丢帧 4帧(64ms) 3帧(48ms) 5帧(80ms) 2帧(32ms) 2帧(32ms) 3帧(48ms) 3帧(48ms) 2帧(32ms) 2帧(32ms) 3帧(48ms) 2帧(32ms) 1帧(16ms) 多次丢帧,累计32帧,但是每次时间较短,卡顿感受不不 明显 卡顿明显 平稳滑动 57帧并不不⼀一定⽐比56帧体验好
  • 20.卡顿指标和慢函数 慢函数 丢帧 FPS •展示大盘趋势 •数据波动较小 •不容易发现卡顿问题 •非真实用户体验 •卡顿衡量指标 •丢帧分布和趋势 •真实的用户体验 •多维度展示 •定位到函数级 •聚合函数调用链 •解决具体卡顿问题
  • 21.丢帧分布和趋势 丢帧分布 丢帧趋势
  • 22.流畅度分布 针对4.x系统上的流畅度分布
  • 23.流畅度、丢帧统计 针对4.x系统上的数据统计
  • 24.慢函数 监控函数级执行速度,将执行较慢的函数调用链聚合,定位到具体原因,解决导致卡顿的问题
  • 25.缓存监控 监控缓存中存在的大文件、大文件夹和过期文件
  • 26.缓存空间恶化原因 • 开发人员多 • 多团队协同 • 沟通同步成本大 人员 代码 • 历史代码 • 缓存失效bug • 兼容问题 • 需求快速迭代 • 功能下线未处理缓存 • 业务复杂,收敛困难 功能 测试 • 不容易复现 • 非真实用户场景 • 导致空间占用过大 缓存
  • 27.线上缓存监控 针对App私有⽬目录中缓存空间过⼤大的监控 ⼤大⽂文件监控 • 单⽂文件⼤大⼩小超过阈值 ⼤大⽂文件夹监控 • ⽂文件夹占⽤用过多 • 包含⽂文件数量量过多 过期⽂文件监控 • 过期⽂文件 ⻓长时间没执⾏行行过更更新 ⼤大⽂文件夹 ⼤大⽂文件
  • 28.⼤大⽂文件缓存监控 App缓存文件大小超过阈值,上报聚合统计
  • 29.⼤大⽂文件夹缓存监控 App缓存文件夹大小或者包含文件数量超过阈值,上报聚合统计
  • 30.电量量监控 监控多进程,前后台,不同方式的耗电情况
  • 31.应⽤用耗电 计算耗电⽅方式 ⼿手机系统估算 Screen CPU Bluetooth • ⾮非真实数据 • 相对指标可参考 电流计 电量 GPS WiFi • 硬件设备,真实电量量 • 系统和应⽤用混合 • 环境不不好搭建 Battery Historian Radio Video/ Audio • 线下测试
  • 32.⼿手机系统耗电估算 Screen DisplayManagerService DisplayController 采⽤用估算⽅方式计算耗电量量 power_profile.xml Wakelock • 按照不不同类型分别估算 PowerManagerService • 系数参照power_profile.xml Notifier WiFi … 的声明 BatteryStatsImpl 耗电量 • 不不同机型⼚厂商参数不不同 • 应⽤用获取不不到电量量细节数据 … … 总耗电 = CPU毫秒 * 系数1 + 流量量Bytes * 系数2 + ...
  • 33.power_profile.xml -- 耗电估算参数01001420.3356901604120220888830017013907033……• 编译在/system/framework/frameworkres.apk中 • 分别声明了了不不同类型设备发⽣生不不同动作时的耗电 量量系数 • 包括Bluetooth,WiFi,Radio,CPU等
  • 34.BatteryStatsHelper.java中关键⽅方法 private void processAppUsage(SparseArrayasUsers) { final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null); mStatsPeriod = mTypeBatteryRealtime; BatterySipper osSipper = null; final SparseArray uidStats = mStats.getUidStats(); final int NU = uidStats.size(); for (int iu = 0; iu < NU; iu++) { final Uid u = uidStats.valueAt(iu); final BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 0); mCpuPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); mWakelockPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); mWifiPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); mSensorPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); mCameraPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType); final double totalPower = app.sumPower(); if (DEBUG && totalPower != 0) { Log.d(TAG, String.format("UID %d:total power=%s", u.getUid(), makemAh(totalPower))); } } } ... // code 按照不同类型分别估算
  • 35.耗电模块 关注的主要耗电模块,然后根 Location 据自定义的全局指数估算 Alarm Wakelock • Location:计时 • Alarm:计次 耗电模块 • Net:计算值 Net Sensor • Wakelock:计时 • CPU:计算值 CPU
  • 36.耗电监控架构 进程1 采 集 进程2 前台 CPU 后台 流量量 存 储 Wakelock Location Alarm 后台 前台 … … … 多进程收集信息传递给 ContentProvider(主进程收集存储) 主进程,⽽而后继续具体 计 算 上 报 前台总时⻓长 前台总耗电 前台模块耗电 前台模块单位时间耗电 前台单位时⻓长总耗电 上报接⼝口 … … 耗电评分,⽽而后上报
  • 37.监控系统展示
  • 38.异常流量量监控 监控不同的业务消耗流量的情况
  • 39.异常流量量监控
  • 40.监控流量量范围 HTTP 系统中所有的HTTP请求 WebView 详情⻚页等使⽤用WebView场景 Socket ⻓长链接等场景 视频 观看⼩小视频,⻄西⽠瓜视频流量量 直播 游戏直播等
  • 41.流量量监控实现 线上监控上报和服务端日志组合 Phone Server 结合上报异常流量量数据,业务后端下游数据,在监控平台聚合,展示
  • 42.监控平台展示 异常流量中,播放视频流量的消耗
  • 43.单点问题追查
  • 44.⽤用户反馈单点问题 配合度低 环境复杂 • 描述不不清楚 交流不不便便 • 反复沟通成本⾼高 • 不不容易易复现 个例例偶现 单点问题 描述差异 • 机型、环境差异
  • 45.⽇日志库 为了了定位线上的问题,在⽤用户反馈时,经⽤用户授权⽇日志回 崩溃日志 传,然后上传。⽽而⽇日志存储是⼀一个频繁且数据量量⼤大的操作, 且⽂文件IO和db⼜又不不能满⾜足性能的需求,基于这样的背景,⾃自 研了了⼀一套基于mmap的⽇日志库ALog,主要特性: 调试日志 网络日志 业务日志 ⽇日志分类 • 格式化输出 • ⾼高速写⼊入 • ⽇日志压缩 • ⽇日志加密 • 前端可视化
  • 46.⽇日志库架构 App 接入层 业务 组件 崩溃 … ALog中间件 Java层 格式化 名单过滤 ALog库分Java层和Native层两部分, 基本压缩、加密下沉到Native层,便便 于跨平台使⽤用。 ⾃自动⾏行行号 App中多个业务、组件都接⼊入ALog, 抽象出来中间件的概念,其它组件和 compress ALog Native层 业务接⼊入中间件,调⽤用接⼝口写⼊入⽇日志。 encrypt 具体实现类的注⼊入,在宿主App中实 Sharding Gzip AES AutoClean mmap … 现。
  • 47.⽇日志流 用户反馈时根据用户授权,回传日志 解决问题 • 调试日志重定向到日志流 排查问题 ⽤用户反馈 • 形成线上和线下一体化 • 用户反馈,研发解决问题联动 • 监控平台权限收敛 单点查询 ⽇日志回传
  • 48.⽇日志流 • ⽇日志⽅方便便输出 • 不不必关⼼心展现存储细节 • 上报与展示完整闭环 ALog上报 • ⽤用户反馈时授权上报 聚合数据 业务,组件写⼊入⽇日志,反 馈时授权上传 存储服务将数据存储、聚 合 服务端接收 服务接收后⾃自动解 压,解密 监控平台 前端展示,⽤用户维度查询
  • 49.监控系统查看ALog
  • 50.未来畅想 更更健全的功能 覆盖更更加健全的性 能专项,稳定性、 不不合理理代码的线上 监控。 更更准确的定位问题 更更深层次的监控⼒力力 度,能够直接定位 到具体问题的原因 线上问题主动发现 多⽅方⾯面,多维度,多技 术的监控、更更强⼤大的报 警和归因能⼒力力,主动发 现线上问题
  • 51.
  • 52.
  • 53.