使用代码热点诊断慢调用链的问题

ARMS代码热点作为一种监控诊断工具,通过持续剖析技术定时采集请求线程堆栈快照,真实还原代码执行的第一现场。

使用场景

  • 当促销活动出现慢调用时,ARMS代码热点可为您快速定位问题代码。

  • 当系统出现大量慢调用时,ARMS代码热点可为您自动保存第一现场。

  • 当业务太复杂,偶发性慢调用无法复现时,ARMS代码热点可为您还原代码真实方法层面的执行轨迹。

  • 当调用链中因为缺失对应用代码非框架层面的方法埋点时,代码热点帮您还原对应缺失埋点的实际方法调用耗时。

前提条件

  • 代码热点功能需要使用3.1.4或以上版本的探针。

  • 代码热点功能依赖持续剖析能力,对操作系统内核和JDK版本有一定的要求,请参见使用限制,选择合适的操作系统与JDK。

  • 目前仅同步调用的调用链支持使用代码热点功能,包含异步调用的调用链暂不支持。

开启代码热点

  1. 登录ARMS控制台,在左侧导航栏选择应用监控 > 应用列表

  2. 应用列表页面顶部选择目标地域,然后单击目标应用名称。

    说明

    语言列的图标含义如下:

    Java图标:接入应用监控的Java应用。

    image:接入应用监控的Golang应用。

    image:接入应用监控的Python应用。

    -:接入可观测链路 OpenTelemetry 版的应用。

  3. 在左侧导航栏中单击应用设置,然后单击自定义配置页签。

  4. 持续剖析区域开启总开关,然后开启代码热点开关,配置需要开启的应用实例的IP地址或者一组实例所属的网段地址。

    重要

    该功能从4.2.x版本探针起默认开启,低版本未配置过该功能的应用在控制台也会显示为开启状态,但实际需要单击保存才会正式生效。

  5. 在页面底部单击保存

    无需重启应用即可生效。

通过接口调用查看代码热点数据

示例:解析并遍历JSON数据及调用下游HTTP接口。

public class HotSpotAction extends AbsAction {

  private RestTemplate restTemplate = new RestTemplate();
  
  //请求入口方法
  @Override
  public void runBusiness() {
    readFile();
    invokeAPI();
  }

  //执行HTTP调用
  private void invokeAPI() {
    String url = "https://httpbin.org/get";
    String response = restTemplate.getForObject(url, String.class);
  }

   //读取文件数据并解析
  private double readFile() {
    InputStreamReader reader = new InputStreamReader(
        ClassLoader.getSystemResourceAsStream("data/xxx.json"));
    LinkedList<Movie> movieList = GSON.fromJson(reader, new TypeToken<LinkedList<Movie>>() {
    }.getType());
    double totalCount = 0;
    for (int i = 0; i < movieList.size(); i++) {
      totalCount += movieList.get(i).rating();
    }
    return totalCount;
  }
}
  1. 登录ARMS控制台,在左侧导航栏选择应用监控 > 应用列表

  2. 应用列表页面顶部选择目标地域,然后单击目标应用名称。

    说明

    语言列的图标含义如下:

    Java图标:接入应用监控的Java应用。

    image:接入应用监控的Golang应用。

    image:接入应用监控的Python应用。

    -:接入可观测链路 OpenTelemetry 版的应用。

  3. 在左侧导航栏中单击接口调用,并在页面右侧选择目标接口,然后单击调用链查询页签。

  4. 调用链查询页签上单击目标TraceId链接。

  5. 详情列中单击放大镜图标,然后单击代码热点页签。

    image

    图中左侧为本次调用中所涉及的所有方法耗时情况列表,右侧为对应方法所有方法栈信息绘制的火焰图。其中:

    • Self列表示方法在自身的调用栈中所消耗的时间或资源,不包括其子方法调用所消耗的时间或资源。可以用于识别哪些方法在自身内部花费了大量的时间或资源。

    • Total列包含方法自身消耗的时间或资源,及其所有子方法调用所消耗的时间或资源。可以帮助了解整个方法调用栈中哪些方法贡献了最多的时间或资源。

    作为排查具体的热点代码逻辑,可以通过重点关注Self列或直接查看右侧火焰图中底部的较宽火苗从中定位到高耗时的业务方法,较宽火苗是引发上层耗时高的根源,一般是系统性能的瓶颈所在,例如上图中的java.lang.Thread.sleep()方法。

    根据上图,进行如下分析:

    1. Self值从大到小排列,找到并单击Self值最大的方法java.util.LinkedList.node(int),右侧火焰图中将会聚焦相关方法。

      image

    2. 聚焦后可以发现,java.util.LinkedList.node(int)就是右侧火焰图中的最宽栈顶方法。

    3. 由于该栈顶是JDK中的库函数,并非为业务方法,因此,沿着栈顶方法java.util.LinkedList.node(int)从下往上搜索,依次经过java.util.LinkedList.get(int) > com.alibaba.cloud.pressure.memory.HotSpotAction.readFile(),而com.alibaba.cloud.pressure.memory.HotSpotAction.readFile()属于所分析应用的业务方法,即为第一个所分析应用自身定义的方法行,其耗时为3.75s,占整张火焰图的69.88%,因此com.alibaba.cloud.pressure.memory.HotSpotAction.readFile()是该火焰图所采集时段内资源占用较高的显著瓶颈所在,可以根据该方法名,对业务中相关方法的逻辑进行梳理,查看是否存在优化空间。

常见问题

  • 代码热点的耗时为什么会出现小于本次请求耗时情况?

    为了尽可能降低代码热点功能对应用性能的影响,我们引入了采集优化机制,这会导致统计到的耗时小于实际的请求耗时。统计偏差一般在20毫秒内,您可以忽略绝对值的偏差,重点关注相对耗时最高的方法。

  • 代码热点的统计范围是否有限制?

    • 对于耗时超过15分钟的请求,代码热点功能仅提供前15分钟的分析数据。

    • 对于低耗时请求(一般在500ms以下,根据系统负载动态决策),为了降低系统开销,ARMS不会触发代码热点数据统计,因此,可能会出现页签中无数据的现象。