2. 应用启动追踪

Spring Boot应用的启动过程可能包含复杂的组件图、启动阶段和资源初始化步骤。追踪这些步骤能帮助我们理解启动各阶段的时间消耗,加深对上下文生命周期和启动序列的认识。

Spring Framework本身提供了记录应用启动和图初始化的功能,而Spring Boot Actuator则通过HTTP或JMX提供生产级监控能力。从Spring Boot 2.4开始,应用启动追踪指标已通过/actuator/startup接口开放。

3. 环境配置

要启用Spring Boot Actuator,先在POM中添加spring-boot-starter-actuator依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

同时添加spring-boot-starter-web依赖以支持HTTP访问:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>3.1.2</version>
</dependency>

application.properties中暴露所需接口:

management.endpoints.web.exposure.include=startup

后续我们将使用curljq工具调用接口并解析JSON响应。

4. Actuator接口

要捕获启动事件,需配置@ApplicationStartup接口的实现。默认情况下,应用上下文使用空操作实现(no-op),不执行任何追踪以最小化开销。因此,与其他Actuator接口不同,这里需要额外配置

4.1 使用BufferingApplicationStartup

将启动配置设置为BufferingApplicationStartup实例。这是Spring Boot提供的内存实现,捕获启动过程中的事件并存储在内部缓冲区

@SpringBootApplication
public class StartupTrackingApplication {

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(StartupTrackingApplication.class);
        app.setApplicationStartup(new BufferingApplicationStartup(2048));
        app.run(args);
    }
}

这里指定了2048的缓冲区容量。当事件数达到此上限后,将停止记录。需根据应用复杂度合理设置容量,只有配置此实现后,启动接口才可用

4.2 startup接口

启动应用后,可通过POST方式调用/actuator/startup接口:

> curl 'http://localhost:8080/actuator/startup' -X POST | jq
{
  "springBootVersion": "2.5.4",
  "timeline": {
    "startTime": "2021-10-17T21:08:00.931660Z",
    "events": [
      {
        "endTime": "2021-10-17T21:08:00.989076Z",
        "duration": "PT0.038859S",
        "startTime": "2021-10-17T21:08:00.950217Z",
        "startupStep": {
          "name": "spring.boot.application.starting",
          "id": 0,
          "tags": [
            {
              "key": "mainApplicationClass",
              "value": "com.baeldung.startup.StartupTrackingApplication"
            }
          ],
          "parentId": null
        }
      },
      {
        "endTime": "2021-10-17T21:08:01.454239Z",
        "duration": "PT0.344867S",
        "startTime": "2021-10-17T21:08:01.109372Z",
        "startupStep": {
          "name": "spring.boot.application.environment-prepared",
          "id": 1,
          "tags": [],
          "parentId": null
        }
      },
      ... 其他步骤省略
      {
        "endTime": "2021-10-17T21:08:12.199369Z",
        "duration": "PT0.00055S",
        "startTime": "2021-10-17T21:08:12.198819Z",
        "startupStep": {
          "name": "spring.boot.application.running",
          "id": 358,
          "tags": [],
          "parentId": null
        }
      }
    ]
  }
}

响应包含详细的启动事件列表,每个步骤包含名称、开始/结束时间及耗时信息。注意:接口调用后会清空缓冲区,需重启应用才能再次获取完整数据,建议保存响应供后续分析。

4.3 过滤启动事件

缓冲区容量有限,可通过过滤只存储感兴趣的事件:

BufferingApplicationStartup startup = new BufferingApplicationStartup(2048);
startup.addFilter(startupStep -> startupStep.getName().matches("spring.beans.instantiate"));

使用addFilter方法仅记录名称匹配指定模式的步骤。

4.4 自定义追踪

可继承BufferingApplicationStartup实现自定义追踪逻辑。由于这类功能通常用于测试环境而非生产,建议通过系统属性在空操作实现和缓冲实现间切换。

5. 分析启动耗时

实际场景中,我们常需定位启动耗时较长的Bean(如缓存加载、数据库连接池初始化等)。调用接口后,用jq处理数据,筛选名称为spring.beans.instantiate的步骤并按耗时排序:

> curl 'http://localhost:8080/actuator/startup' -X POST \
| jq '[.timeline.events
 | sort_by(.duration) | reverse[]
 | select(.startupStep.name | match("spring.beans.instantiate"))
 | {beanName: .startupStep.tags[0].value, duration: .duration}]'

该命令执行以下操作:

  1. duration降序排序事件
  2. 筛选名称匹配spring.beans.instantiate的步骤
  3. 提取beanNameduration生成新JSON对象

输出示例:

[
  {
    "beanName": "resourceInitializer",
    "duration": "PT6.003171S"
  },
  {
    "beanName": "tomcatServletWebServerFactory",
    "duration": "PT0.143958S"
  },
  {
    "beanName": "requestMappingHandlerAdapter",
    "duration": "PT0.14302S"
  },
  ...
]

发现resourceInitializer Bean耗时约6秒,这是影响启动速度的关键因素。通过此方法可快速定位问题,但需注意ApplicationStartup仅用于启动阶段,不能替代Java性能分析器

6. 总结

本文介绍了在Spring Boot应用中获取和分析启动指标的方法:

  1. 配置Actuator接口并启用启动追踪
  2. 解析接口返回的详细启动事件
  3. 通过示例演示如何分析启动耗时

完整代码可在GitHub仓库获取。


原始标题:Spring Boot Startup Actuator Endpoint