APM 与线上监控
☆ APM 把 “性能优化” 从本地经验升级成线上体系. 你的风控 SDK 背景可以重点讲 native crash, 采样, 灰度和宿主影响控制. 方法论, 指标口径与跨域取舍见 性能优化总览.
一, APM 监控什么
| 类型 | 指标 | 关键证据 |
|---|---|---|
| Crash | Java crash, native crash, 崩溃率 | 堆栈, 版本, 机型, ABI |
| ANR | 主线程阻塞, 输入超时, 广播 / Service 超时 | traces, 主线程栈, 锁等待 |
| 卡顿 | 慢帧, 冻结帧, 帧率 | Choreographer/FrameMetrics/Perfetto |
| 启动 | 冷启动, 首帧, 可交互时间 | launch trace, 业务埋点 |
| 网络 | 成功率, 耗时, 错误码, 弱网 | interceptor, DNS/TLS/connect/read 分段 |
二, Crash 监控: Java 与 Native
Java crash 常通过 Thread.setDefaultUncaughtExceptionHandler 捕获; native crash 需要 signal handler 或 Breakpad/Crashpad 这类方案.
面试边界: 不要说 “所有 native crash 都能优雅恢复”.多数情况下只能采集现场, 下次启动上报, 灰度回滚.
三, ANR 与卡顿监控
- ANR: 系统判定, 重点是拿到 traces 和主线程阻塞证据.
- 卡顿: 应用侧可用主线程 watchdog, Choreographer 帧回调, FrameMetrics 监控.
- 线上采样必须控制开销, 避免监控本身制造卡顿.
四, 启动与网络监控
启动监控要区分进程创建, Application, 首 Activity, 首帧, 业务首页可交互. 网络监控要拆 DNS, TCP, TLS, 请求, 响应, 解析, 不要只报一个总耗时.
五, 上报链路, 采样与隐私
- 本地缓存: 避免 crash 当场丢失日志.
- 批量上报: 减少电量和流量开销.
- 采样: 高频事件不能全量.
- 隐私: 不上传明文 token, 手机号, 身份证, 设备敏感字段.
六, 怎么把 SDK 经历讲成亮点
可以这样组织:“SDK 嵌入宿主 App 后, 我关注的不只是功能成功, 还要保证不拖累宿主稳定性. 我们会监控 native crash, 初始化耗时, 线程/网络开销, 灰度阶段看指标, 异常时能按版本/ABI/机型聚合定位.”
七, 线上定位闭环: 符号化, 聚类, 告警与回归检测
APM 面试的加分点是讲清 “采集之后怎么用”.只说捕获 crash 不够, 还要能把问题聚类, 定位, 告警, 灰度拦截.
Crash 符号化流水线
| 环节 | 关键数据 | 作用 |
|---|---|---|
| 构建归档 | versionCode, git sha, mapping, native symbols, build id | 确保线上堆栈能还原 |
| 崩溃采集 | Java stack, signal, 寄存器, 线程栈, ABI, 机型 | 保留现场 |
| 符号化 | R8 mapping, so debug symbols | 还原混淆方法和 native 函数 |
| 聚类 | 崩溃栈 fingerprint (稳定归类键: 规范化后的相同故障栈使用同一键), 版本, 设备维度 | 找 Top N 问题而不是看单条日志 |
Native crash 尤其要强调 build id / 符号文件匹配. 没有对应版本的 symbols, 即使采集到了 tombstone 也很难定位.
ANR 与卡顿聚类
- ANR 不只看主线程栈, 还要看锁等待, Binder 调用, IO, CPU 占用和发生场景.
- 卡顿指标要用慢帧/冻结帧比例, P90/P99, 不要只报平均 FPS.
- traces 需要按主线程栈 fingerprint 聚类, 否则线上会被大量重复样本淹没.
Dashboard 与告警
| 指标 | 常用维度 | 典型用途 |
|---|---|---|
| crash-free users/sessions | 版本, 渠道, 系统, 机型, ABI | 判断能否继续灰度 |
| ANR 率 | 页面, 进程, 系统版本 | 发现主线程 / 厂商 ROM 问题 |
| 启动 P50/P90/P99 | 冷/温/热启动, 渠道, 机型档位 | 识别长尾体验 |
| 网络成功率 / 耗时 | 域名, 接口, 地区, 网络类型 | 区分端侧和服务端问题 |
告警不要只设固定阈值, 还要看环比 / 同比和灰度版本对照. 小流量灰度时, 一个新增 Top crash 比整体 crash 率更敏感.
Breadcrumb 与隐私脱敏
客户端可维护轻量环形日志, 记录页面跳转, 关键按钮, 网络错误, 配置版本等 breadcrumb. 注意只记录定位所需上下文, 不要写入 token, 手机号, 身份证, 精确位置等敏感数据.
APM SDK 自身开销
APM SDK 也要被监控: 初始化耗时, 线程数, 磁盘占用, 上报流量, 采样命中率, 丢弃原因. 面试可以说 “监控系统本身不能成为性能问题”.
采样, 聚合与告警预算
APM 与本地专项的分工是线上发现, 分群和归因. 客户端采样要稳定且可解释, 敏感日志最小化并有保留/删除策略; Crash/native 符号, mapping 和 build-id 必须与制品可追溯. 指标按版本, 设备, OS, 地区和网络聚合, 告警定义窗口, 分母, 基线和错误预算, 避免少量异常样本触发全量回滚.
可执行闭环: 符号还原到灰度验证
以下命令在本机构建产物齐全时使用, 路径和 ABI 必须与线上制品精确匹配; 输出仅说明用途, 不代表已执行或真实 crash. 命令中的 mapping.txt, obfuscated-stack.txt, app/build/intermediates/..., libfeature.so, tombstone.txt 和 0xOFFSET 都是必须替换的输入占位符. 其中 mapping 和混淆栈必须来自同一 Java 构建; 符号目录, so, tombstone 必须匹配 ABI/build-id, so 必须是未 strip 符号文件; 0xOFFSET 必须是根据正确 load bias 计算的该 so 相对地址.
retrace mapping.txt obfuscated-stack.txt
ndk-stack -sym app/build/intermediates/.../arm64-v8a -dump tombstone.txt
addr2line -Cfpe libfeature.so 0xOFFSET
retrace 用同一构建的 R8 mapping.txt 将混淆 Java 栈还原; ndk-stack 用未 strip 符号处理 tombstone; addr2line 把与正确 so build-id 对应的地址定位到函数 / 行. 若 mapping, 符号, ABI, load bias 或 build-id 不匹配, 输出不可作为定位证据. 构建阶段应归档这些制品并为每个上传事件记录 versionCode, build-id 与符号版本.
卡顿采样应只在超过定义阈值或按稳定采样率时收集轻量主线程栈/页面上下文, 设置时间与大小上限, 禁止采集敏感输入. 将 Java/native crash, ANR 和卡顿按规范化栈的 fingerprint (指稳定归类键) 聚类, 再按新增版本, 影响用户数, 核心路径和设备集中度排序.
假设性闭环演练, 非线上事件: 灰度版本出现新增 fingerprint, 先核验符号化与分母, 比较同机型的对照版本; 若影响扩大, 暂停放量并用远程开关降级, 保留样本与 trace. 修复进入下一小批灰度后, 观察 crash-free users, ANR rate, 启动分位数和该 fingerprint 的趋势, 满足预设观察窗口才扩大; 异常反弹则回滚. 这样避免用单条日志或平均值宣布问题已解决.
高频面试题
Q1: 线上卡顿怎么监控? 答: 轻量方案是主线程 watchdog 或 Choreographer 统计慢帧; 深入定位要结合 Perfetto/trace. 线上只采样关键指标, 本地复现再做完整 trace.
Q2: native crash 怎么定位? 答: 采集 signal, 寄存器, 线程栈, so build id/版本/ABI, 结合未 strip 符号或 symbol server 还原堆栈, 按版本和机型聚类.
Q3: APM SDK 会不会影响性能? 答: 会, 所以要采样, 异步, 批量, 延迟上报, 关键路径不做重 IO, 监控逻辑本身也要被监控.
Q4: 线上 crash 很多时怎么快速定位优先级? 答: 先按版本, 机型, ABI, 系统和堆栈 fingerprint 聚类, 看新增问题, 影响用户数, 是否阻断核心路径. Java crash 用 mapping 还原, native crash 用 build id 匹配 symbols. 灰度期如果出现新增 Top crash, 应暂停放量并用配置降级或热修复处理.
Q5: 为什么性能指标要看 P90/P99 而不只看平均值? 答: 平均值会掩盖长尾问题. 移动端用户体验常被低端机, 弱网, 特定 ROM 拉垮, 所以启动, 卡顿, 网络耗时都要看分位数和维度拆分.
易错点 / 追问
- 不要把本地 Profiler 等同于线上 APM.
- 不要上传敏感业务数据.
- 不要只讲采集, 还要讲聚合, 告警, 灰度回滚和修复闭环.