Перейти к основному содержимому

Конечная точка активатора запуска Spring Boot

· 6 мин. чтения

1. Введение

Приложения Spring Boot могут иметь сложные графы компонентов, этапы запуска и этапы инициализации ресурсов.

В этой статье мы рассмотрим, как отслеживать и контролировать эту информацию о запуске через конечную точку Spring Boot Actuator .

2. Отслеживание запуска приложений

Отслеживание различных шагов во время запуска приложения может предоставить полезную информацию, которая может помочь нам понять время, затрачиваемое на различных этапах запуска приложения . Такой инструментарий также может улучшить наше понимание жизненного цикла контекста и последовательности запуска приложения .

Spring Framework предоставляет функциональные возможности для записи запуска приложения и инициализации графа. Кроме того, Spring Boot Actuator предоставляет несколько возможностей мониторинга и управления производственного уровня через HTTP или JMX.

Начиная с Spring Boot 2.4 , метрики отслеживания запуска приложений теперь доступны через конечную точку /actuator/startup .

3. Настройка

Чтобы включить Spring Boot Actuator, давайте добавим зависимость spring-boot-starter-actuator в наш POM:

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

Мы также добавим зависимость spring-boot-starter-web , так как это необходимо для доступа к конечным точкам через HTTP:

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

Кроме того, мы также предоставим требуемую конечную точку через HTTP, установив свойство конфигурации в нашем файле application.properties :

management.endpoints.web.exposure.include=startup

Наконец, мы будем использовать curl и jq для запроса конечной точки HTTP привода и анализа ответа JSON соответственно.

4. Конечная точка привода

Чтобы фиксировать события запуска, нам нужно настроить наше приложение с реализацией интерфейса @ApplicationStartup . По умолчанию ApplicationContext для управления жизненным циклом приложения использует неактивную реализацию. Это, очевидно, не выполняет инструментарий запуска и отслеживание для минимальных накладных расходов.

Поэтому, в отличие от других конечных точек привода, нам нужна дополнительная настройка .

4.1. Использование BufferingApplicationStartup

Нам нужно установить конфигурацию запуска приложения на экземпляр BufferingApplicationStartup. Это реализация в памяти интерфейса ApplicationStartup , предоставляемого Spring Boot. Он фиксирует события во время процесса запуска Spring и сохраняет их во внутреннем буфере .

Начнем с создания простого приложения с такой реализацией для нашего приложения:

@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. Конечная точка запуска

Теперь мы можем запустить наше приложение и запросить конечную точку привода запуска .

Давайте используем curl для вызова этой конечной точки POST и отформатируем вывод JSON с помощью jq :

> 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.foreach.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
}
},
... other steps not shown
{
"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
}
}
]
}
}

Как мы видим, подробный ответ JSON содержит список инструментированных событий запуска. Он содержит различные сведения о каждом шаге, такие как имя шага, время начала, время окончания, а также сведения о времени выполнения шага . Подробная информация о структуре ответа доступна в документации по веб-API Spring Boot Actuator .

Кроме того, полный список шагов, определенных в контейнере ядра, и дополнительные сведения о каждом шаге доступны в справочной документации Spring .

Важно отметить, что последующие вызовы конечной точки не предоставляют подробный ответ JSON. Это связано с тем, что вызов конечной точки запуска очищает внутренний буфер. Поэтому нам потребуется перезапустить приложение, чтобы вызвать ту же конечную точку и снова получить полный ответ.

При необходимости мы должны сохранить полезную нагрузку для дальнейшего анализа .

4.3. Фильтрация событий запуска

Как мы видели, реализация буферизации имеет фиксированную емкость для хранения событий в памяти. Поэтому может быть нежелательно хранить в буфере большое количество событий.

Мы можем фильтровать инструментированные события и сохранять только те, которые могут нас заинтересовать:

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

Здесь мы использовали метод addFilter только для инструментов с указанным именем.

4.4. Пользовательские инструменты

Мы также можем расширить BufferingApplicationStartup , чтобы обеспечить настраиваемое поведение отслеживания запуска в соответствии с нашими конкретными потребностями в инструментарии.

Поскольку этот инструментарий особенно ценен в средах тестирования, а не в производственной среде, использование системного свойства и переключение между неоперативной и буферной или пользовательскими реализациями является простым упражнением.

5. Анализ времени запуска

В качестве практического примера попробуем идентифицировать любой экземпляр компонента во время запуска, инициализация которого может занять относительно много времени. Например, это может быть загрузка кеша, пул соединений с базой данных или какая-то другая дорогостоящая инициализация во время запуска приложения.

Мы можем вызвать конечную точку, как и раньше, но на этот раз мы будем обрабатывать вывод с помощью 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}]'

Приведенное выше выражение обрабатывает ответ JSON для извлечения информации о времени:

  • Отсортируйте массив timeline.events в порядке убывания.
  • Выберите все шаги, соответствующие имени spring.beans.instantiate из отсортированного массива.
  • Создайте новый объект JSON с beanName и продолжительностью каждого соответствующего шага.

В результате вывод показывает краткий, упорядоченный и отфильтрованный вид различных bean-компонентов, созданных во время запуска приложения:

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

Здесь мы видим, что bean- компонент resourceInitializer при запуске занимает около шести секунд. Это можно рассматривать как значительный вклад в общее время запуска приложения. Используя этот подход, мы можем эффективно определить эту проблему и сосредоточиться на дальнейшем исследовании и возможных решениях .

Важно отметить, что ApplicationStartup предназначен для использования только во время запуска приложения . Другими словами, он не заменяет профилировщики Java и платформы сбора метрик для инструментирования приложений .

6. Заключение

В этой статье мы рассмотрели, как получить и проанализировать подробные метрики запуска в приложении Spring Boot.

Во-первых, мы увидели, как включить и настроить конечную точку Spring Boot Actuator. Затем мы рассмотрели полезную информацию, полученную от этой конечной точки.

Наконец, мы рассмотрели пример для анализа этой информации, чтобы лучше понять различные шаги во время запуска приложения.

Как всегда, код для этой статьи доступен на GitHub .