refer
1-intro
这个一般分为问题的定位 和 问题的解决,这里专注于问题的解决.
2-ApplicationStartup
这个工具是 Spring 官方提供的, 子类 用于收集各个启动阶段的 StartupStep 数据, 例如如下的阶段:
- 应用上下文生命周期 (基础包的扫描, 配置类管理)
bean的生命周期 (实例化, 智能初始化, 后处理)- 应用事件处理
默认的实现 DefaultApplicationStartup 是一个空实现, 没有性能损失. SpringBoot 原生还支持2种. 一种 Buffered 的,一种 JFR 的 .
3-Custom Log StartUp Implementation
一个最简单的实现,用于测试其原理.
import org.slf4j.LoggerFactory
import org.springframework.core.metrics.ApplicationStartup
import org.springframework.core.metrics.StartupStep
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicInteger
import java.util.function.Supplier
class CustomApplicationStartup : ApplicationStartup {
val inProgressSteps = ConcurrentHashMap<Long, StartupStep>()
val stepDurations = ConcurrentHashMap<String, Long>()
val counter = AtomicInteger()
override fun start(name: String): StartupStep {
val step = CustomStartupStep(name, System.nanoTime(), counter.incrementAndGet().toLong())
inProgressSteps.put(Thread.currentThread().threadId(), step)
return step
}
companion object {
private val log = LoggerFactory.getLogger(CustomApplicationStartup::class.java)
}
inner class CustomStartupStep(
val _name: String,
val startTime: Long,
val _id: Long,
val tags: MutableList<StartupStep.Tag> = ArrayList<StartupStep.Tag>(),
) : StartupStep {
override fun getName(): String = _name
override fun getId(): Long = _id
override fun getParentId(): Long? = null
override fun tag(key: String, value: String): StartupStep {
this.tags.add(DefaultTag(key, value))
return this
}
override fun tag(
key: String,
value: Supplier<String>
): StartupStep = tag(key, value.get())
override fun getTags(): StartupStep.Tags = StartupStep.Tags {
tags.iterator()
}
override fun end() {
val duration = (System.nanoTime() - startTime) / 1_000_000L
stepDurations.put(_name, duration)
inProgressSteps.remove(Thread.currentThread().threadId())
log.info("step end: $_name, duration: $duration, tags: ${tags.joinToString(", ")}")
}
}
data class DefaultTag(
private val _key: String,
private val _value: String
) : StartupStep.Tag {
override fun getKey(): String = _key
override fun getValue(): String = _value
}
}4-Spring acutator startup endpoint
1)-开启 startup 端点
management:
endpoints:
web:
exposure:
include: startup2)-使用 BufferedStartUp 记录
val app = SpringApplication(ExampleApp::class.java)
// app.applicationStartup = CustomApplicationStartup()
app.applicationStartup = BufferingApplicationStartup(10240)
app.run(*args)- 注意,
10240是内存中的 cap, 超过了,新的step不会记录
如果要加过滤, 使用如下配置:
BufferingApplicationStartup startup = new BufferingApplicationStartup(2048);
startup.addFilter(startupStep -> startupStep.getName().matches("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}]'