稳定性,是工程师的生存线。
我在团队中除了负责核心需求开发外,还负责技术创新(前文介绍的KMP)和性能优化(前文介绍的卡顿监控优化),稳定性等。这篇文章想聊一次我经历过的最棘手、最惊心动魄的线上稳定性事故。
它涉及:
-
SIGABRT / EGL_BAD_ALLOC / native 内存 OOM
-
无业务堆栈、无 debug 信息
-
大规模真实用户、不可回滚的线上互动功能
-
多团队协作 & 错位 blame
-
热修复策略受限 + 紧急曲线救国
更重要的是,如何从海量日志 + 极端场景 + 业务行为分析 中"抓到最后一根稻草",并把它证明成唯一真相。我负责项目的整体稳定性,Bugly 实时监控,Native 崩溃跟踪,灰度风险验证,历史问题溯源 & 定点治理。我每天到公司第一件事就是先看Bugly,跑我写的分析脚本,查看告警,了解我们的产品质量。
背景:直播互动场景复杂,稳定性责任重大
在我们教育直播 App 里,直播间有"小组讨论"场景:每个小组 6 人,平时只拉老师和课件流,老师触发互动 → 学生开始拉同组同学的音视频流,讨论结束 → 关闭拉流。看似普通需求,但它涉及动态 RTC 路径切换、频繁 Surface 创建/销毁、底层内存抖动。
当问题发生时,我必须第一时间识别、定位、响应。
事故:崩溃率一天暴涨 10 倍
某天晚上正在写代码的我,收到了bugly的报警:App 崩溃率突然大幅跃升,且集中在同一直播课程类型。我们第二天的整体崩溃率直接拉升了10倍。 Bugly 报警如下:
ini
SIGABRT
Failed to create context, error = EGL_BAD_ALLOC
- 0
#00 pc 00000000000a8904 /apex/com.android.runtime/lib64/bionic/libc.so (abort+164) [arm64-v8a::a7f4c875f4091d6d01501d20016ac2a8]
- 11
#11 pc 00000000000ac350 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) [arm64-v8a::a7f4c875f4091d6d01501d20016ac2a8]
堆栈近乎没有业务信息:一个字:冷 。我立马意识到,这不是常见的 Java OOM,而是: EGL_BAD_ALLOC = 显存/Surface 资源耗尽 → native OOM 。再结合 SIGABRT,非常明确:某个 native 资源未释放,导致 EGL 环境创建失败
但崩溃堆栈里没有明确业务代码,没有 RTC 相关调用,没有 Cocos 痕迹,没有崩溃上下文。这是最难排的一类事故:无栈 + native + 难复现
第一步:从日志里"复盘用户的一天"
我们有实时上传的全量日志 (自研接入链路),我抽取多个崩溃用户完整课时日志轨迹:
包括session 行为序列,课程类型,设备内存变化,拉流与 Surface 生命周期,垃圾回收行为,CPU/MEM 峰值点
发现:
| 观察点 | 结果 |
|---|---|
| 崩溃设备 | 分散,无设备集中问题 |
| 发生场景 | 用户都做过新的互动(动态拉同组流,且互动了多次),还有Cocos题,H5互动题等 |
| 内存趋势 | PSS 持续上升,峰值后立即崩溃 |
| 用户行为 | 不集中(说明不是单场景代码 crash) |
第二步:锁定"共性" → 不断假设 &证伪
我第一时间把怀疑点定位到我们的新互动题上(拉关同组所有人视频流,且互动了多次)。我们业务的几个内存消耗大户,流媒体,Cocos互动题,H5互动题,以前都发生过较为严重的内存泄漏。因为我负责直播间的播放器和流媒体的开发,且新上线的这个互动和流媒体重度相关,所以第一时间拉流媒体老师排查。如果流媒体没问题,则下一步需要去找Cocos团队和FE团队确认业务变更。崩溃堆栈的不明确,给问题的解决带来了巨大困难。
我将崩溃日志按课堂维度进行聚类分析(我们业务规模足够大):
| 场景 | 崩溃比例 |
|---|---|
| 普通直播 | 几乎无崩溃 |
| 带小组讨论课堂 | 集中出现 |
这就让我意识到: 崩溃与小组音视频"动态拉流"强相关 。说明我刚开始的怀疑方向是对的。 这是典型内存泄漏 + 场景触发 + 极端堆叠。
第三步:复现:一次复现就能锁死真凶
我用测试账号密集操作拉同组流这个互动 ,并观察:PSS 变化,Surface 数量,EGL context 数量,GPU 内存占用。最终成功复现: 每次互动讨论 → Surface 未释放 → 累计增长 → EGL 内存耗尽 → 崩溃。
抓到真凶了。
第四步:多团队共查 → 二分删代码确认
拉上流媒体负责人一起排查,是他们最近上线的一个拉流优化 导致:老师结束互动时,学生 Surface 对象没有释放 ,而这一优化是最近一个版本才上线,我们开发联调阶段根本没接到这段代码****
第五步:热修复?不够!只能曲线救国
问题出在 C++ 层,我们的热修复只能 patch Kotlin 层。怎么办?我想了一个策略,下发kotlin热修复包,更改互动逻辑。业务上层只拉用户音频流,不拉视频流。用户无感,虽然功能降级但彻底避免崩溃 同时:紧急修复bug, 小比例强升版本。针对课程粒度大规模推送。最终崩溃曲线极速回落
事故总结:定位思路与方法论
1. 先判断类型
EGL_BAD_ALLOC = GPU/Surface 泄漏
2. 用用户行为还原现场
日志复盘用户的行为
3. 找共性
跨用户 + 跨课程聚类
4. 激进复现
模拟真实课堂、重复操作
5. 找到"最后一根稻草
OOM 永远不是最终崩点,而是"积累崩溃模型"
6. 架构级降级策略
必要时牺牲体验换活着
教育直播类 App 稳定性体系
| 类目 | 手段 |
|---|---|
| 监控 | Bugly、自研全量日志、PSS/Surface 监控、native signal 处理 |
| 定位 | 日志链路复原、行为聚类、设备维度剥离、二分验证 |
| 治理机制 | Feature Flag、动态策略、灰度实验、强升 |
| 研发规范 | RTC 场景 Surface 生命周期 checklist、C++ 资源自测脚本 |
| 演练 | 内存 stress 测试、极端设备测试、反复操作场景 |
特别适用于RTC + 多引擎 + 多端融合的业务场景。
崩溃率暴涨不可怕,可怕的是没有应对危机情况的能力
稳定性不是"碰运气",是系统工程,它涉及
- 全链路监控
- 行为洞察能力
- 技术直觉
- 极致复现
- 果断降级
- 多团队推动能力
这次经历让我更加深刻地理解:稳定性建设不是一次性的任务,而是一种长期的工程方法论。它要求我们在日常开发中不断思考、优化和预防,也促使团队形成科学的事故响应流程、复盘机制和可量化的稳定性指标。正是这种不断迭代和沉淀的能力,帮助我们在复杂业务场景下保持产品可靠,同时也提升了每一位工程师面对未知挑战时的判断力与执行力。