作者:饶子豪,杨龙。
随着软件技术的发展和迭代,很多企业软件系统已经逐渐从单体应用演化为云原生微服务架构,一方面让应用实现高并发、易扩展、高开发敏捷性,但另一方面也让软件应用链路越来越长,依赖的外部技术越来越多, 并使一些在线问题的解决变得困难。
虽然经过十几年的发展,分布式系统对应的可观测性技术发展迅速,很多问题都得到了一定程度的解决,但有些问题还是难以定位的
图1 CPU持久性峰值。
图 2 堆内存空间用于 **。
图 3 跟踪链无法找到耗时过程的根本原因。
如何找到上述问题的根本原因?
对于一些有较多故障排除经验,对各种故障排除工具有更多接触的朋友,对于上述问题,他们可能会想到以下排查方法:
1.对于CPU峰值诊断,请使用CPU热点火焰图工具进行故障排除;
2.对于内存问题,可以使用内存快照来诊断内存使用情况。
3.为了解决慢调用链的诊断过程缺少时间的问题,可以使用 arthas 提供的 trace 命令来诊断该方法耗时。
以上解决方案有时可以解决一些问题,但有排查相关问题经验的朋友也要知道,它们有自己的门槛和局限性,比如:
1.对于测试环境下难以重现的在线问题,CPU热点火焰图工具也无能为力;
2.内存快照不仅可能影响在线应用程序的稳定性,而且需要使用相关工具诊断问题的丰富经验。
3.当慢速调用链不稳定且难以追踪时,arthas 的 trace 命令变得难以排除故障,并且很难定位多个应用和多机器的调用请求。
有没有一种相对简单高效的强大诊断技术可以帮助我们解决上述问题? 答案是本文后面将介绍的连续分析技术。
什么是连续分析?
通过连续分析实时动态捕获应用程序、CPU、内存等资源应用程序的堆栈信息以提供帮助监控和定位应用程序性能瓶颈。通过上面的介绍,连续分析的概念是不是还比较模糊? 如果很多朋友之前没有听说过持续性能分析,那么 jdk 提供的 jstack 打印线程方法栈,定位当前状态的工具,过去可能或多或少被很多朋友在排查应用问题时碰到了:
图 4 JStack 工具。
持续分析其实和jstack类似,它也是抓取应用线程在一定频率或阈值下执行的CPU、内存等资源应用栈信息,然后通过一些可视化技术呈现相关信息,这样我们就可以更直观地洞察应用相关资源的使用情况。 说到这里,可能使用较多性能分析工具的朋友会联想到火焰图:
图 5 火焰图工具。
通常,在压力测试过程中,通过手动打开或关闭来实际使用一次性性能诊断工具,例如 Arthas CPU 热点火焰图生成器即时分析技术,无论是在数据收集方法方面,还是在数据呈现形式方面,都与将要介绍的连续分析技术基本相同。 与连续剖析相比,核心区别在于它是即时的,而不是“持续的”。
有火焰图使用经验的朋友,大家可以回想一下,我们一般在压力测试场景中都会使用CPU热点火焰图工具,在压力测试过程中,会使用一些工具捕获并应用一段时间的火焰图进行压力测试性能分析。 而持续剖析不仅仅是解决压测场景的性能观察,更重要的是更重要通过一些技术优化,我们可以以低成本的方式持续分析应用的资源使用情况以及应用的整个运行生命周期,然后通过火焰图或其他可视化方式呈现出比可观测技术更深的可观测性效果。
不断剖析实现原则
说完连续剖析的基本概念,大家一定对连续剖析的实现原理感到好奇,然后简单介绍一些相关的实现原理。 我们知道,跟踪是通过在关键执行路径上埋藏方法来收集调用中的信息来收集调用中的信息,以恢复调用中的参数返回值和异常耗时等信息,但业务应用很难详尽地列出所有埋藏点, 如果埋点太多,开销会非常大,因此很难完全覆盖上图3介绍的跟踪监控盲点问题。持续分析的实现,其实就是把JDK库中一些与资源应用相关的关键位置埋在较低层次,或者依靠操作系统的特定事件来实现信息采集,这样既能实现低开销,又能对采集到的信息有更强的洞察效果。
比如CPU热点分析,一般的思路是通过操作系统底部的系统调用获取CPU上正在执行的线程的信息,然后在一定频率(比如10ms)采集一个线程对应的方法栈信息,1s就可以采集到100个线程的方法栈信息, 类似于下面的图 6,最后对这些方法栈做一些处理,最后使用火焰图等一些可视化技术来展示,这就是 CPU 热点分析结果。当然,以上只是一些实现原理的简单说明,不同性能分析引擎的技术实现和需要剖析的对象一般都存在细微差别。
图6 连续剖析数据采集原理。
除了常见的CPU热点火焰图分析外,其实对于计算机中各种系统资源的使用和应用,连续分析技术可以提供相应的分析结果,帮助分析相关资源的应用和实现原理(注意,不同的性能分析实现技术可能不同):
可视化技术的连续解剖
火焰图是连续分析后使用最广泛的数据可视化技术之一,那么火焰图的奥秘是什么呢?
什么是火焰图?
火焰图是一种可视化分析工具,可帮助开发人员跟踪和显示进行程序函数调用所花费的时间。 核心思想是将程序的函数调用方法堆栈转换为矩形的“火焰”图像,每个矩形的宽度代表函数使用的资源比例,高度代表函数的整体调用深度。 通过比较不同时间点的火焰图,可以快速诊断程序的性能瓶颈并相应地进行优化。
广义的火焰图有两种类型,一种是函数方法堆栈的底部元素在底部,顶部元素在顶部的狭义火焰图,如下左图所示,另一种是冰柱形火焰图,底部元素在顶部,顶部元素在堆栈底部, 如下图右图所示。
图7 各种类型的火焰图。
如何使用火焰图?
火焰图作为一种用于性能分析的可视化技术,只能在了解应该如何读取的基础上用于性能分析。 例如,对于一个 CPU 热点火焰图,这个问题最常见的表达式之一就是看火焰图中是否有宽的堆栈顶部,这个说法背后的原因是什么?
事实上,这是因为火焰图绘制的是计算机中执行的方法堆栈。 在计算机中调用函数的上下文基于称为堆栈的事物堆栈的数据结构的特点是元素先进后出,所以堆栈的底部是初始调用函数,上部是被叫子函数的逐层。 当最后一个子函数在堆栈顶部执行时,它会从上到下从上到下不在堆栈中,所以堆栈的顶部很宽,这意味着子函数需要很长时间才能执行,而它下面的父函数也会需要很长时间,因为它已经执行过了,不能立即退出堆栈。
图 8 堆栈数据结构。
因此,分析火焰图的方法步骤如下:
1.确定火焰图对应的类型,找到烟囱顶部的方向;
2.如果火焰图的总资源使用率较高,请继续检查火焰图的堆栈顶部是否有较宽的部分;
3.如果堆栈顶部较宽,则沿着堆栈顶部搜索到堆栈底部,找到第一个包名称和分析应用程序本身定义的方法行,然后重点检查此方法是否有优化空间。
下面是一个资源使用率较高的火焰图,火焰图中的性能瓶颈步骤如下:
1.下图的形状可以看作是一个冰柱形的火焰图,烟囱的底部在上,烟囱的顶部在底部,因此需要自下而上地进行分析。
2.分析下面的堆栈顶部,可以发现右边堆栈顶部较宽的是右侧的方法:j**autil.linkedlist.node(int)。
3.由于堆栈的较宽顶部是 JDK 中的库函数,而不是业务方法,因此它遵循堆栈方法的顶部:j**autil.linkedlist.node(int),自下而上搜索,传递:j**autil.linkedlist.get(int)->com.alibaba.cloud.pressure.memory.hotspotaction.readfile() 和 comalibaba.cloud.pressure.memory.hotspotaction.readfile() 是属于被分析应用程序的业务方法,即为第一个被分析的应用程序本身定义的方法行,它需要 389s,占整个火焰图的7606%,所以是火焰图采集期间资源占用高的最大瓶颈,所以业务中相关方法的逻辑可以根据相关方法的名称进行梳理,看是否有优化的余地。 此外,图的左下角还可以与上述分析方法j**a进行对比net.socketinputstream 相关方法,发现它属于分析应用的第一个自定义父方法,全称是 comalibaba.cloud.pressure.memory.hotspotaction.invokeapi,约占总数的 23%。
图9 火焰图分析过程。
经过以上介绍,您应该对连续分析的概念、数据采集原理和可视化技术有一定的了解。 接下来,我们来谈谈ARMS提供的开销和持续分析能力如何帮助排查和定位各种在线问题。
ARMS提供一站式持续分析能力,已实现近1W应用案例的持续数据采集和监控**。
图10 ARMS继续剖析产品功能。
左图是当前ARMS从上到下、数据采集、数据处理、数据可视化等持续分析能力的概述。 在具体功能层面,针对用户需求最迫切的几个场景,如CPU和堆内存分析、CPU和内存热点功能等,提供了相应的解决方案。 对于慢呼叫链的诊断,ARMS提供了热点功能。 与一般分析方案相比,具有开销低、粒度细、方法堆栈齐全等特点。
相应子功能的最佳实践已在ARMS产品文档中提供
有关诊断高 CPU 使用率的详细信息,请参阅使用 CPU 热点诊断高 CPU 使用率>> 诊断问题。 关于如何诊断高堆内存使用率,请参见使用内存热点诊断高堆内存使用率>> 诊断问题。 关于呼叫链时间的根本原因诊断,请参阅 “使用热点诊断呼叫链慢速”。>> 诊断问题。 客户案例
自相关功能发布以来,更好的辅助用户诊断一些长期困扰在线的疑难杂症,得到了众多用户的好评,如:
1.用户 A 发现,某应用服务刚启动时,前几个请求会很慢,使用链路诊断耗时的分发时会出现监控盲点。 最后,利用 arms ** 热点帮助它诊断出 sharding-jdbc 框架初始化时间过长导致的相关调用链慢的耗时根本原因,最终帮助它找出困扰已久的现象的根本原因。
图11 用户问题诊断案例1
2.用户 B,在压测过程中,应用的所有实例中总会有一些节点的响应时间比其他节点慢很多,使用跟踪时无法看到根本原因。 然后,根据相关信息,检查应用环境日志采集组件的资源使用情况,发现压测时占用了大量的CPU,导致应用实例写入日志缺乏资源竞争,导致请求处理缓慢。
图12 用户问题诊断案例2
3.用户C,在在线应用运行过程中,发现堆内存占用量总是很大,通过内存热点,很快就发现应用使用的微服务框架版本在订阅的上游服务信息运行过程中被使用,导致堆内存占用量大, 然后咨询相关的框架服务商,最后了解问题可以通过升级框架版本来解决。
图13 用户问题诊断案例3
开销
最后,大家可能非常关心武器连续剖析的成本,所以我们设计了下面这个函数的压测场景来计算这个函数的成本,模拟一个从压测中心到业务入口应用的请求,查询查询数据库并返回结果。
图14 压力试验示意图。
测试环境使能所有连续分析功能,并使用 K8s 容器运行时环境来模拟一般的企业应用运行时环境。 Pod 限制值为 4c8g,4g 堆中年轻一代的比例设置为 1 2,压力限制为 6000 tps。 测试500tps的开销和4800tps下极限压力的80%如下表所示。 从表中可以看出,所有功能开启后 CPU 开销约为 5%,堆内内存开销不明显,堆外内存占用约为 50MB,流量较小,或者仅启用部分持续分析功能时会更低。
图15 压力测试结果。
据了解,很多企业应用在运行过程中对CPU内存等资源的利用率都比较低,通过少量的资源消耗,为应用提供新的观察视角,让应用在应用运行异常时有详细的根本原因定位数据,是非常有价值的!
如果你对ARMS的持续分析功能感兴趣,欢迎加入钉钉关于ARMS持续分析的小组讨论。 (组号:22560019672)。
直播推荐:
掌握ARMS持续分析 - 轻松洞察应用性能瓶颈:
相关链接:
1] 堆栈。2] 使用 CPU 热点诊断 CPU 消耗过高。
3] 使用内存热点来诊断堆内存使用率过高。
4] 使用热点诊断慢速呼叫链。