从3月中旬到前几天,我的工作重心一直在符号还原服务的重构上;整个重构从提案、方案设计,到难点攻关、核心功能实现,最后到功能验证,性能优化以及搭建监控和压力测试。全程的体验可以说是历尽艰险,但也成就感满满
符号还原系统的开发告一段落,这里我就遵循空雨伞的思考方式来总结下整个重构工作
Caused by: java.lang.Exception: Exception at the end of the call at ly.count.android.demo.a.b(SourceFile:29) at ly.count.android.demo.a.a(SourceFile:21) at ly.count.android.demo.ActivityExampleCrashReporting.c(SourceFile:98) at ly.count.android.demo.ActivityExampleCrashReporting.b(SourceFile:94) at ly.count.android.demo.ActivityExampleCrashReporting.a(SourceFile:90) at ly.count.android.demo.ActivityExampleCrashReporting.onClickCrashReporting10(SourceFile:82)
Caused by: java.lang.Exception: Exception at the end of the call at ly.count.android.demo.Utility.void DeepCall_b()(SourceFile:29) at ly.count.android.demo.Utility.void DeepCall_a()(SourceFile:21) at ly.count.android.demo.ActivityExampleCrashReporting.void deepFunctionCall_3()(SourceFile:98) at ly.count.android.demo.ActivityExampleCrashReporting.void deepFunctionCall_2()(SourceFile:94) at ly.count.android.demo.ActivityExampleCrashReporting.void deepFunctionCall_1()(SourceFile:90) at ly.count.android.demo.ActivityExampleCrashReporting.void onClickCrashReporting10(android.view.View)(SourceFile:82)
项目中的老翻译服务的呈现形式为分端实现,各端分别部署微服务提供翻译服务(consumer, web);这造成了很多额外的运维成本和维护成本,实际上大部分的翻译层逻辑是相同的,核心逻辑都是地址 / 混淆后符号到符号的映射,在redis缓存,符号表管理以及符号表监控等方面也都可以使用统一的架构和解决方案。在这些前提下,统一的符号还原模块成为了可能
实现符号还原大一统的大前提是统一各端符号表,要实现各端形式各异的符号表到统一格式的能实现高效率翻译的符号表的转换
统一符号表格式后,则要考虑符号表的管理,在历史各端符号还原服务的生命历程中,有不少的问题都是符号表管理不当导致的,排查起来也相当痛苦。在统一的符号还原服务中,符号表的管理粒度依旧为产品 → 版本号 → 符号表,但需要依据MECE原则设计完整的符号表生命周期,保证整个符号表系统的可控性和可维护性
其次是符号表缓存结构设计,通过redis缓存减少实际高并发翻译中与符号表的文件IO,减轻服务器压力的同时增加整个翻译服务的吞吐量
在符号表的相关难点攻克后,我们才能开始真正的符号翻译,整个翻译流程需要保证架构和实现的健壮性,高性能,可维护性,以支持实际翻译服务中的各种需求
最后是监控方案的设计与实现,首先要基设计翻译服务的SLI以及SLO,再基于翻译流程接口以及符号表生命周期进行监控埋点,完成翻译层监控Dashboard的配置
思路清晰,开始行动
首先要解决我们统一符号还原的大前提——将各端的符号表转换为统一格式的符号表
各端的符号表基本可以分为内存地址形和混淆形符号表,本质都是映射关系(内存地址 -> 符号 / 混淆符号 -> 原始符号)
那么要统一这两类符号表,我们的思路就是将混淆类的混淆符号首先转换为数字,这里就要面对两个问题:
经过多次技术评审后最终我们攻克了这里的技术难关,完成了符号表统一;在后续的过程中面临的架构设计、监控埋点等一系列其它难题时,团队内也是不断通过技术评审集思广益,解决关键问题的同时保证信息差最小,一一攻克难关
在主要的技术难点都基本确定了解决方案后,我们开始了正式的统一符号还原模块的开发。
基于已经设计好的满足 golang 标准项目结构的包结构设计,逐个完成各核心 package 的开发(symbolmap, transform, translate等),以及 library 内部分基础组建的更新(kafka, redis分布式锁, resource等),并编写完善的单元测试。在完成符号还原模块的主体部分后迅速开始交叉测试工作,最终在3个星期内完成了符号还原模块本身的交付
在符号还原模块开发的过程中受到了整个项目大版本交付进度的催促,而在符号还原模块这个开发阶段,大家都还沉浸在性能优化与监控完善方面的工作中,有些忽略了整体进度;后续也是及时将数据流验证提到最高优先级,在较短的时间内完成了各数据流的验证,交付项目后,再捡起符号还原模块本身的性能优化和监控系统完善的工作,继续完成了大文件符号表转换的性能优化,符号表管理api接口拓展,部分架构调整以及监控指标建设等工作
在最终的符号还原模块交付前,我们继续做了私有云环境的适配工作以及整个符号还原模块的系统测试和压力测试,同时验证监控面板、监控指标的有效性。在不间断的测试过程中,也发现了符号还原服务接入的部分数据流上的适配问题,以及在kafka版本上的适配问题等关键问题,完成了及时的修复,同时也不断迭代优化项目内的符号表生命周期、翻译流程等,完善监控面板,保证一眼就能抓住关键信息。
重新以空雨伞的视角审视这次的符号还原服务重构工作,less & well 如下:
后期的进度把控以及测试工作上还有待改进
前期的工作动机、思路整理和核心开发工作都差强人意