你好,从今天起我们就开始了对最后部分“APM 软技能”的学习了。在本部分的课程中,我会从典型案例故事出发,让你感同身受问题现场,然后加之相应软技能辅助,让你在前面课程中学习到的硬实力更加充分地发挥出来,最终让你独自排查问题时可以更有头绪,具备独当一面的能力。

解决常规的应用服务问题或是故障排查手段,每个团队都有着适用于自身的“白皮书”或是“三板斧”。其主要作用,就是让一线开发人员遇到问题时,具备定位常规问题的思路,并能梳理好团队已有的 APM 工具以及项目干系人,从而做到处理问题有章法、解决问题有效率。

然而 APM 工具自身出现问题时,如何去排查解决,是不在“白皮书”中的。所以我就将近些年,我在解决 APM 客户端自燃问题上的经验和方法与你分享。

“断链”案例

根据“10 | 亲和线程模型:分布式链路追踪,学习 SkyWalking 就够了”,我们知道分布式链路追踪是 APM 的重要领域,分布式链路追踪最常见的就是链路串联不起来,也就是“断链”问题。

那我们今天就学习一个案例,看看问题排查过程是怎样的。

在某个时间节点后,有业务线反馈 SkyWalking 的监控链路的部分数据丢失,起初我刚开始接到问题时,第一反应就是认为其出现了“插件不支持”或“内存泄漏造成断链”问题,这也是“10 | 亲和线程模型:分布式链路追踪,学习 SkyWalking 就够了”讲述过的。

所以我根据上报问题的内容,去查看了分布式链路的存储数据;并通过在线剖析工具,去跟进任务线程的监控周期。最终发现并没有出现内存泄漏,链路模型也被“完整”地收集到了后端收集器。

但是当使用链路 ID 查看链路的存储模型时,我发现丢失的数据为部分组件的监控数据,比如 Apache HttpClient 组件,那便排除了内存泄漏问题。剩下常见的诱发问题的原因,便就是“插件不支持”,然后通过 maven 分析应用程序的依赖树,我却发现组件的版本是支持的。

这时再向下开展工作就遇到了僵局,如果缺乏解决 APM 问题经验的话,再往下解决问题的思路就会变得没有章法。

所以接下来,我就介绍几招通用的“三板斧”,让你解决问题的能力更上一个维度。

复现问题

1.摸清现场

在上文“断链”案例中,我的第一步排查手段就是及时去摸清现场,这与常规解决应用服务线上问题,以及常规的解题思维是一致的。

在时间上,任何问题的现场,第一手案发资料都会随着时间流逝愈发模糊,所以要尽早排查摸清问题现场,那如何摸清现场呢?

这时就要使用 APM 工具了,APM 工具不仅可以排查业务服务这些非自身问题,也可以通过 APM 工具来排查 APM 工具自身的问题,正如用 Arthas 排查现场是否有内存泄漏问题的思考是一致的。

关于工具如何选择,你可以回顾“08 | 工匠精神:熟悉 APM 产品的能力是 RD 的分内之事”。

总之工具掌握越透彻,现场记录就越清晰,这也是一个持续精进的过程。所以每次定位、解决问题的过程都需要你反复思考。

2.增加拟真度

现场记录完毕后,就是进行问题复现了。能复现的问题,就是可以解决的问题。APM 技术栈只是相对应用服务技术栈相较冷门而已,如果能稳定复现 APM 工具引发应用服务自燃的问题,那这个问题肯定就能被解决。

那复现案发现场的关键因素是什么呢?和业务问题复现一样,就是相似度。相似度越高,复现的成功率就越高,这也是核心场景都有沙箱环境的原因之一。通过极致的拟真线上,沙箱环境可用于验证功能是否具备线上预期的完整性。如果你的应用服务没有沙箱环境,那可以根据问题类型的不同,去针对性地增强某些方面的拟真性。

正如线上业务流程问题,需要增加数据方向的拟真需要。APM 属于基础设施,那就需要增加线上环境配置的拟真性。那针对线上 APM 环境,由于线上配置很多,我首先将线上 SkyWalking 配置进行拟真。很不巧,没有复现问题。

那接下来继续升级拟真度,公司线上环境往往是有多个无侵入的探针配置的,将所有探针配置进行拟真。结果问题在调试环境可以稳定复现,基于对比复现问题的过程,问题也逐渐明朗。单个 SkyWalking 探针不会有问题,多个探针并存的情况存在着一些兼容性问题,

问题复现后,可以根据问题的复杂度,让更多资源加入去并行解决问题。我建议出现问题的业务线和负责 APM 建设的技术人员都进行资源投入,在调试环境下进行问题的并行分析。而分析的最常规有效手段就是开启调试模式,那如何去调试探针程序呢?

调试探针程序

业务系统开启调试很简单,就是通过调整依赖框架暴露出的日志级别,进行启动调试模式。但是字节码增强技术怎么开启调试模式呢?

首先探针日志调试模式的开启只是一方面,另一方面就是对字节码加载前后的过程。开启调试,对于 SkyWalking 而言,可以通过开启 agent.is_open_debugging_class 参数,让探针在检测到的类文件保存在 /debugging 文件夹中,以解决兼容问题。

以此类推,通过打开每个探针的修改后的类文件,我发现 SkyWalking 的增强代码并没有生效。

有了线索,那接下来就是找出其中原因了。那问题就可以确定了,当两个探针同时增强一个类时,它的加载顺序是什么样的呢?根据设计常识得知,加载顺序是根据探针的先后次序,那事实真的如此吗?

验证思路非常简单,我调整了两个探针的位置,发现 SkyWalking 探针依然无法实现增强。

解决思路

由于使用 APM 用户少,所以 APM 引发的问题很难在搜索引擎中找到相关的解决方案。本次问题也是一样,我之前的认知一直是:探针对目标类增强的过程,是根据命令行中探针的次序决定的。但根据复现场景,以及前后调整两个探针的不同顺序,发现结论与认知相悖。

这是 Oracle 官方都对探针字节码增强过程的详细解释,我就不详细进行原文翻译了。

其核心翻译是:类文件的转换是通过类似优先级队列的数据结构实现的,在每个优先级队列里,探针的类加载过程与我们认知一致,就是在同一个优先级队列中的探针,加载顺序按照次序进行顺序加载。但在反复进行多个优先级队列的类转化的场景中,不会调用没有转换能力的类转换器。

显然 SkyWalking 探针满足了这个规则,所以造成在与其他优先级队列共存的类转换场景下,没有生效的。

那你可能会问,为什么是四个优先级队列呢?原因非常简单,因为决定类转换器有如下两个布尔属性。

Can-Redefine-Classes(是否可以重新定义类)

Can-Retransform-Classes(是否可以重新转换类)

这两个属性可以有四种组合,也就对应着四种优先级队列。

找到原因了,修复问题也变得轻而易举。我们只需要将两个探针的优先级队列,调整到统一队列中,然后这些探针会按顺序加载类转换器。

那应该是用哪个优先级队列呢?

确认修复

我的想法是用兼容性较高的优先级队列,就是使用支持重新转换功能的类加载器的优先级队列。那之后使用 Apache SkyWalking 发起讨论 issue 的模板,将故障现场、故障原因、文献内容等相关信息发起讨论;和多方 review 后,将解决方案合到仓库主干,这样在上百个公司的用户案例下,此功能的改造会被进一步验证兼容性。

本案例详细的现场描述,解决思路我都写在了 SkyWalking 的社区,你可以点击链接,查看详细查看我与社区一起完成的修复过程

小结与思考

今天的课程,先讲述了在使用 SkyWalking 过程中,出现的“断链”的场景,虽然问题案例涉及知识点超出了已有认知,我们也不必慌张,坚信“可以复现的问题都是可以被解决的问题”这一信念,去复现问题。

复现问题最重要的就是复现环境与案发环境的环境相似度,开发人员根据问题的类型去针对性的拟真线上:

对于业务问题,拟真可以专注于数据方向;

对于 APM 问题,可以专注于系统环境,拟真到一定程度后,问题就会被复现。

有了复现的场景后,就可以根据问题的复杂度,投入更多的研发资源去并行解决问题,针对 APM 探针引发的问题,我们可以通过摸清案例、复现问题、开启调试、与社区共同解决这四个步骤,最终将问题解决。

你在工作开发中遇到过哪些 APM 工具自身的问题呢?你解决问题的思路是什么?最后效果怎么样?欢迎在评论区写下你的思考,期待与你的讨论。

-– ### 精选评论