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
后续我们将使用curl
和jq
工具调用接口并解析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}]'
该命令执行以下操作:
- 按
duration
降序排序事件 - 筛选名称匹配
spring.beans.instantiate
的步骤 - 提取
beanName
和duration
生成新JSON对象
输出示例:
[
{
"beanName": "resourceInitializer",
"duration": "PT6.003171S"
},
{
"beanName": "tomcatServletWebServerFactory",
"duration": "PT0.143958S"
},
{
"beanName": "requestMappingHandlerAdapter",
"duration": "PT0.14302S"
},
...
]
发现resourceInitializer
Bean耗时约6秒,这是影响启动速度的关键因素。通过此方法可快速定位问题,但需注意ApplicationStartup
仅用于启动阶段,不能替代Java性能分析器。
6. 总结
本文介绍了在Spring Boot应用中获取和分析启动指标的方法:
- 配置Actuator接口并启用启动追踪
- 解析接口返回的详细启动事件
- 通过示例演示如何分析启动耗时
完整代码可在GitHub仓库获取。