Русский
Русский
English
Статистика
Реклама

Перевод Изучение и анализ Spring Boot приложения с помощью Actuator и jq

Spring Boot Actuator помогает нам отслеживать и управлять нашими приложениями в производственной среде.Он предоставляет конечные точки, которые публикуются показатели работоспособности и другая информация о запущенном приложении.Мы также можем использовать его для изменения уровня ведения журнала приложения, создания дампа потока и т. д. - короче говоря, возможностей, которые упрощают работу в производственной среде.

Хотя Actuator в основном используется в производственной среде, он также может помочь нам во время разработки и сопровождения.Мы можем использовать его для изучения и анализа нового приложения Spring Boot.

В этой статье мы увидим, как использовать некоторые из его конечных точек для изучения нового приложения, с которым мы не знакомы.Мы будем работать в командной строке и использоватьcurl иjq, с изящным и мощным JSON процессором командной строки.

Пример кода

Эта статья сопровождается примером рабочего кодана GitHub.

Зачем использовать Actuator для анализа и изучения приложения?

Представим, что мы впервые работаем над новой кодовой базой на основе Spring Boot.Мы, вероятно, изучим структуру папок, посмотрим на имена папок, проверим имена пакетов и имена классов, чтобы попытаться построить модель приложения в нашем уме.Мы могли бы сгенерировать некоторые UML диаграммы, чтобы помочь определить зависимости между модулями, пакетами, классами и т. д.

Хотя это важные шаги, они дают нам только статичное представление о приложении.Мы не можем получить полную картину, не понимая, что происходит во время выполнения.Например, что представляют собой все создаваемые Spring Beans?Какие конечные точки API доступны?Каковы все фильтры, через которые проходит запрос?

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

Общий обзор Spring Boot Actuator

Начнем с краткого обзора по Spring Boot Actuator.

На верхнем уровне, когда мы работаем с Actuator, мы делаем следующие шаги:

  1. Добавляем Actuator как зависимость к нашему проекту

  2. Включаем и открываем конечные точки

  3. Защищаем и настраиваем конечные точки

Давайте кратко рассмотрим каждый из этих шагов.

Шаг 1. Добавьте Actuator зависимость

Добавление Actuator в наш проект похоже на добавление любой другой зависимости.Вот фрагмент для Mavenpom.xml:

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

Если бы мы использовали Gradle, мы бы добавили вфайл build.gradle следующийфрагмент:

dependencies {    implementation 'org.springframework.boot:spring-boot-starter-actuator'}

Простое добавление указанной выше зависимости в приложение Spring Boot предоставляет некоторые готовые конечные точки, такие как/actuator/health, которые могут использоваться, например, для поверхностной проверки работоспособности с помощью балансировщика нагрузки.

$ curl http://localhost:8080/actuator/health{"status":"UP"}

Мы можем перейти на конечную точку/actuator, чтобы просмотреть другие конечные точки, доступные по умолчанию.Конечная точка /actuator открывает обзорную страницу со всеми доступными конечными точками:

$ curl http://localhost:8080/actuator{"_links":{"self":{"href":"http://localhost:8080/actuator","templated":false},"health":{"href":"http://localhost:8080/actuator/health","templated":false},"health-path":{"href":"http://localhost:8080/actuator/health/{*path}","templated":true},"info":{"href":"http://localhost:8080/actuator/info","templated":false}}}

Шаг 2. Включите и откройте конечные точки

Конечные точки идентифицируются идентификаторами,такими как health, info, metrics и так далее.Включение и открытие конечной точки делает ее доступной для использования попути /actuator URL-адреса приложения, напримерhttp://your-service.com/actuator/health,http://your-service.com/actuator/metrics и т. д.

Большинство конечных точек, за исключениемshutdown, включены по умолчанию.Мы можем отключить конечную точку, установив для свойства management.endpoint.<id>.enabled значениеfalseвapplication.propertiesфайле.Например, вот как мы отключимmetrics конечную точку:

management.endpoint.metrics.enabled=false

Доступ к отключенной конечной точке возвращает ошибку HTTP 404:

$ curl http://localhost:8080/actuator/metrics{"timestamp":"2021-04-24T12:55:40.688+00:00","status":404,"error":"Not Found","message":"","path":"/actuator/metrics"}

Мы можем предоставить доступ к конечным точкам через HTTP и / или JMX.Хотя обычно используется HTTP, для некоторых приложений может быть предпочтительнее JMX.

Мы можем раскрыть конечные точки, установивmanagement.endpoints.[web|jmx].exposure.include для списка идентификаторов конечных точек, которые мы хотим раскрыть.Вот как мы можем открытьmetrics конечную точку, например:

management.endpoints.web.exposure.include=metrics

Чтобы конечная точка была доступна, она должна быть включена и доступна.

Шаг 3. Защитите и настройте конечные точки

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

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

Краткое введение вjq

jq представляет собой JSON-процессор командной строки.Он работает как фильтр, принимая входные данные и производя выходные данные.Доступно множество встроенных фильтров, операторов и функций.Мы можем комбинировать фильтры, направлять выходной сигнал одного фильтра в другой и т. д.

Предположим, у нас в файле есть следующий JSONsample.json:

{  "students": [    {      "name": "John",      "age": 10,      "grade": 3,      "subjects": ["math", "english"]          },    {      "name": "Jack",      "age": 10,      "grade": 3,      "subjects": ["math", "social science", "painting"]    },    {      "name": "James",      "age": 11,      "grade": 5,      "subjects": ["math", "environmental science", "english"]    },    .... other student objects omitted ...  ]}

Это объект, содержащий массив объектов student с некоторыми деталями для каждого ученика.

Давайте рассмотрим несколько примеров обработки и преобразования этого JSON с помощьюjq.

$ cat sample.json | jq '.students[] | .name'"John""Jack""James"

Рассмотримjq команду, чтобы понять, что происходит:

Выражение

Эффект

.students[]

перебиратьмассив students

|

вывод каждогоstudent для следующего фильтра

.name

выборка атрибутаnameиз объектаstudent

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

$ cat sample.json | jq '.students[] | select(.subjects[] | contains("science"))'{  "name": "Jack",  "age": 10,  "grade": 3,  "subjects": [    "math",    "social science",    "painting"  ]}{  "name": "James",  "age": 11,  "grade": 5,  "subjects": [    "math",    "environmental science",    "english"  ]}

Рассмотримкоманду еще раз:

Выражение

Эффект

.students[]

перебирать массивstudents

|

вывод каждогоstudent для следующего фильтра

select(.subjects[] | contains("science"))

выберите студента, если его массивsubjects содержит элемент со строкой наука

С одним небольшим изменением мы можем снова собрать эти элементы в массив:

$ cat sample.json | jq '[.students[] | select(.subjects[] | contains("science"))]'[  {    "name": "Jack",    "age": 10,    "grade": 3,    "subjects": [      "math",      "social science",      "painting"    ]  },  {    "name": "James",    "age": 11,    "grade": 5,    "subjects": [      "math",      "environmental science",      "english"    ]  }]

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

Мы можем использоватьjq как для фильтрации, так и для изменения формы JSON:

$ cat sample.json | jq '[.students[] | {"studentName": .name, "favoriteSubject": .subjects[0]}]'[  {    "studentName": "John",    "favoriteSubject": "math"  },  {    "studentName": "Jack",    "favoriteSubject": "math"  },  {    "studentName": "James",    "favoriteSubject": "math"  }]

Мы, выполнив итерацию по массивуstudents, создали новый объект,содержащий свойствоstudentName иfavoriteSubject со значениями устанавленными из атрибутаname и первогоsubject из исходногообъекта student .В результате мы собрали все новые объекты в массив.

Мы можем многое сделать с помощью нескольких командjq.Поскольку большинство API-интерфейсов, с которыми мы обычно работаем, используют JSON, это отличный инструмент в нашем арсенале инструментов.

Ознакомьтесь сучебникомируководствомиз официальной документации.jqplay- отличный ресурс дляэкспериментови построения нашихjq выражений.

Изучение приложения Spring Boot

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

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

се конечные точки, которые мы увидим, включены по умолчанию. Давйте откроем их:

management.endpoints.web.exposure.include=mappings,beans,startup,env,scheduledtasks,caches,metrics

Использование конечной точки отображения

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

Давайте перейдем на конечную точку с помощью команды curl и направим ответ в jq, чтобы красиво его распечатать:

$ curl http://localhost:8080/actuator/mappings | jq

Вот ответ:

{  "contexts": {    "application": {      "mappings": {        "dispatcherServlets": {          "dispatcherServlet": [            {              "handler": "Actuator web endpoint 'metrics'",              "predicate": "{GET [/actuator/metrics], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}",              "details": {                "handlerMethod": {                  "className": "org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.OperationHandler",                  "name": "handle",                  "descriptor": "(Ljavax/servlet/http/HttpServletRequest;Ljava/util/Map;)Ljava/lang/Object;"                },                "requestMappingConditions": {                  ... properties omitted ...                  ],                  "params": [],                  "patterns": [                    "/actuator/metrics"                  ],                  "produces": [                    ... properties omitted ...                  ]                }              }            },          ... 20+ more handlers omitted ...          ]        },        "servletFilters": [          {            "servletNameMappings": [],            "urlPatternMappings": [              "/*"            ],            "name": "webMvcMetricsFilter",            "className": "org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter"          },          ... other filters omitted ...        ],        "servlets": [          {            "mappings": [              "/"            ],            "name": "dispatcherServlet",            "className": "org.springframework.web.servlet.DispatcherServlet"          }        ]      },      "parentId": null    }  }}

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

jq для дальнейшей фильтрации этой информации. Поскольку мы знаем имена пакетов из нашей службы, jq будет выбирать только те обработчики, которые содержат имя нашего пакета io.reflectoring.springboot.actuator:

Давайте воспользуемся jq для дальнейшей фильтрации этой информации. Поскольку мы знаем имена пакетов из нашей службы, jqselectбудет выбирать только те обработчики, которые содержат имя нашего пакета io.reflectoring.springboot.actuator:

$ curl http://localhost:8080/actuator/mappings | jq '.contexts.application.mappings.dispatcherServlets.dispatcherServlet[] | select(.handler | contains("io.reflectoring.springboot.actuator"))'{  "handler": "io.reflectoring.springboot.actuator.controllers.PaymentController#processPayments(String, PaymentRequest)",  "predicate": "{POST [/{orderId}/payment]}",  "details": {    "handlerMethod": {      "className": "io.reflectoring.springboot.actuator.controllers.PaymentController",      "name": "processPayments",      "descriptor": "(Ljava/lang/String;Lio/reflectoring/springboot/actuator/model/PaymentRequest;)Lio/reflectoring/springboot/actuator/model/PaymentResponse;"    },    "requestMappingConditions": {      "consumes": [],      "headers": [],      "methods": [        "POST"      ],      "params": [],      "patterns": [        "/{orderId}/payment"      ],      "produces": []    }  }}{  "handler": "io.reflectoring.springboot.actuator.controllers.OrderController#getOrders(String)",  "predicate": "{GET [/{customerId}/orders]}",  "details": {    "handlerMethod": {      "className": "io.reflectoring.springboot.actuator.controllers.OrderController",      "name": "getOrders",      "descriptor": "(Ljava/lang/String;)Ljava/util/List;"    },    "requestMappingConditions": {      "consumes": [],      "headers": [],      "methods": [        "GET"      ],      "params": [],      "patterns": [        "/{customerId}/orders"      ],      "produces": []    }  }}{  "handler": "io.reflectoring.springboot.actuator.controllers.OrderController#placeOrder(String, Order)",  "predicate": "{POST [/{customerId}/orders]}",  "details": {    "handlerMethod": {      "className": "io.reflectoring.springboot.actuator.controllers.OrderController",      "name": "placeOrder",      "descriptor": "(Ljava/lang/String;Lio/reflectoring/springboot/actuator/model/Order;)Lio/reflectoring/springboot/actuator/model/OrderCreatedResponse;"    },    "requestMappingConditions": {      "consumes": [],      "headers": [],      "methods": [        "POST"      ],      "params": [],      "patterns": [        "/{customerId}/orders"      ],      "produces": []    }  }}

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

Точно так же мы можем проверить, через какие фильтры проходят наши запросы, прежде чем они достигнут контроллеров:

$ curl http://localhost:8080/actuator/mappings | jq '.contexts.application.mappings.servletFilters'[  {    "servletNameMappings": [],    "urlPatternMappings": [      "/*"    ],    "name": "webMvcMetricsFilter",    "className": "org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter"  },  ... other filters omitted ...]

Использование конечной точкиbeans

Теперь давайте посмотрим на список созданных bean-компонентов:

$ curl http://localhost:8080/actuator/beans | jq{  "contexts": {    "application": {      "beans": {        "endpointCachingOperationInvokerAdvisor": {          "aliases": [],          "scope": "singleton",          "type": "org.springframework.boot.actuate.endpoint.invoker.cache.CachingOperationInvokerAdvisor",          "resource": "class path resource [org/springframework/boot/actuate/autoconfigure/endpoint/EndpointAutoConfiguration.class]",          "dependencies": [            "org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration",            "environment"          ]        },               .... other beans omitted ...    }  }}

Это дает общее представление обо всех компонентах вApplicationContext. Промотр его дает нам некоторое представление о структуре приложения во время выполнения - какие внутренние bean-компоненты Spring, каковы bean-компоненты приложения, каковы их области действия, каковы зависимости каждого bean-компонента и т. д.

Опять же, мы можем использоватьjq для фильтрации ответов и сосредоточиться на тех частях ответа, которые нам интересны:

$ curl http://localhost:8080/actuator/beans | jq '.contexts.application.beans | with_entries(select(.value.type | contains("io.reflectoring.springboot.actuator")))'{  "orderController": {    "aliases": [],    "scope": "singleton",    "type": "io.reflectoring.springboot.actuator.controllers.OrderController",    "resource": "file [/code-examples/spring-boot/spring-boot-actuator/target/classes/io/reflectoring/springboot/actuator/controllers/OrderController.class]",    "dependencies": [      "orderService",      "simpleMeterRegistry"    ]  },  "orderService": {    "aliases": [],    "scope": "singleton",    "type": "io.reflectoring.springboot.actuator.services.OrderService",    "resource": "file [/code-examples/spring-boot/spring-boot-actuator/target/classes/io/reflectoring/springboot/actuator/services/OrderService.class]",    "dependencies": [      "orderRepository"    ]  },  ... other beans omitted ...  "cleanUpAbandonedBaskets": {    "aliases": [],    "scope": "singleton",    "type": "io.reflectoring.springboot.actuator.services.tasks.CleanUpAbandonedBaskets",    "resource": "file [/code-examples/spring-boot/spring-boot-actuator/target/classes/io/reflectoring/springboot/actuator/services/tasks/CleanUpAbandonedBaskets.class]",    "dependencies": []  }}

Это дает представление обо всех компонентах приложения и их зависимостях с высоты птичьего полета.

Чем это полезно?Мы можем получить дополнительную информацию из этого типа представления: например,если мы видим некоторую зависимость, повторяющуюся в нескольких bean-компонентах, вероятно, в нем инкапсулированы важные функции, которые влияют на несколько потоков.Мы могли бы отметить этот класс как важный, который мы хотели бы понять, когда углубимся в код.Или, возможно, этот bean-компонент является God object,который требует некоторого рефакторинга, когда мы поймем кодовую базу.

Использование конечной точкиstartup

В отличие от других конечных точек, которые мы видели, для настройки конечной точкиstartup требуются некоторые дополнительные действия.Мы должны обеспечить реализациюApplicationStartup для нашего приложения:

SpringApplication app = new SpringApplication(DemoApplication.class);app.setApplicationStartup(new BufferingApplicationStartup(2048));app.run(args);

Здесь мы установили для нашего приложенияApplicationStartup значение a,BufferingApplicationStartup которое является структурой в памяти, которая фиксирует события в сложном процессе запуска Spring.Внутренний буфер будет иметь указанную нами емкость - 2048.

Теперь перейдем конечной точкеstartup.В отличие от других конечных точек startup поддерживаетPOST метод:

$ curl -XPOST 'http://localhost:8080/actuator/startup' | jq{  "springBootVersion": "2.4.4",  "timeline": {    "startTime": "2021-04-24T12:58:06.947320Z",    "events": [      {        "startupStep": {          "name": "spring.boot.application.starting",          "id": 1,          "parentId": 0,          "tags": [            {              "key": "mainApplicationClass",              "value": "io.reflectoring.springboot.actuator.DemoApplication"            }          ]        },        "startTime": "2021-04-24T12:58:06.956665337Z",        "endTime": "2021-04-24T12:58:06.998894390Z",        "duration": "PT0.042229053S"      },      {        "startupStep": {          "name": "spring.boot.application.environment-prepared",          "id": 2,          "parentId": 0,          "tags": []        },        "startTime": "2021-04-24T12:58:07.114646769Z",        "endTime": "2021-04-24T12:58:07.324207009Z",        "duration": "PT0.20956024S"      },         .... other steps omitted ....      {        "startupStep": {          "name": "spring.boot.application.started",          "id": 277,          "parentId": 0,          "tags": []        },        "startTime": "2021-04-24T12:58:11.169267550Z",        "endTime": "2021-04-24T12:58:11.212604248Z",        "duration": "PT0.043336698S"      },      {        "startupStep": {          "name": "spring.boot.application.running",          "id": 278,          "parentId": 0,          "tags": []        },        "startTime": "2021-04-24T12:58:11.213585420Z",        "endTime": "2021-04-24T12:58:11.214002336Z",        "duration": "PT0.000416916S"      }    ]  }}

Ответ представляет собой массив с подробной информацией о событиях: name, startTime, endTimeиduration.

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

Поскольку приведенный выше ответ содержит много деталей, давайте сузим его, отфильтровав поspring.beans.instantiate шагу, а также отсортируем события по продолжительности в порядке убывания:

$ curl -XPOST 'http://localhost:8080/actuator/startup' | jq '.timeline.events | sort_by(.duration) | reverse[] | select(.startupStep.name | contains("instantiate"))'$ 

Что здесь произошло?Почему мы не получили ответа?Вызовконечной точки startup также очищает внутренний буфер.Повторим попытку после перезапуска приложения:

$ curl -XPOST 'http://localhost:8080/actuator/startup' | jq '[.timeline.events | sort_by(.duration) | reverse[] | select(.startupStep.name | contains("instantiate")) | {beanName: .startupStep.tags[0].value, duration: .duration}]' [  {    "beanName": "orderController",    "duration": "PT1.010878035S"  },  {    "beanName": "orderService",    "duration": "PT1.005529559S"  },  {    "beanName": "requestMappingHandlerAdapter",    "duration": "PT0.11549366S"  },  {    "beanName": "tomcatServletWebServerFactory",    "duration": "PT0.108340094S"  },  ... other beans omitted ...]

Таким образом, на созданиеbean-компонентовorderController иуходит больше секундыorderService!Это интересно - теперь у нас есть конкретная область приложения, на которой мы можем сосредоточиться, чтобы понять больше.

Командаjq здесь была немного сложнее по сравнению с предыдущими.Давайте разберемся, чтобы понять, что происходит:

jq '[.timeline.events \  | sort_by(.duration) \  | reverse[] \  | select(.startupStep.name \  | contains("instantiate")) \  | {beanName: .startupStep.tags[0].value, duration: .duration}]'

Выражение

Эффект

.timeline.events | sort_by(.duration) | reverse

отсортируйтемассив timeline.events по свойству duration и реверсируйте результат, чтобы он был отсортирован в порядке убывания

[]

перебирать результирующий массив

select(.startupStep.name | contains("instantiate"))

выберите элемент объекта только в том случае, еслисвойствоstartupStep элементаname содержит текст instantiate

{beanName: .startupStep.tags[0].value, duration: .duration}

создать новый объект JSON со свойствами beanName и duration

Скобки над всем выражением указывают на то, что мы хотим собрать все созданные объекты JSON в массив.

Использование конечной точкиenv

Конечная точка env дает обобщенное представление всех свойств конфигурации приложения.Сюда входят конфигурации изapplication.properties файла, системные свойства JVM, переменные среды и т. д.

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

$ curl http://localhost:8080/actuator/env | jq{  "activeProfiles": [],  "propertySources": [    {      "name": "server.ports",      "properties": {        "local.server.port": {          "value": 8080        }      }    },    {      "name": "servletContextInitParams",      "properties": {}    },    {      "name": "systemProperties",      "properties": {        "gopherProxySet": {          "value": "false"        },        "java.class.path": {          "value": "/target/test-classes:/target/classes:/Users/reflectoring/.m2/repository/org/springframework/boot/spring-boot-starter-actuator/2.4.4/spring-boot-starter-actuator-2.4.4.jar:/Users/reflectoring/.m2/repository/org/springframework/boot/spring-boot-starter/2.4.4/spring-boot-starter-2.4.4.jar: ... other jars omitted ... "        },       ... other properties omitted ...      }    },    {      "name": "systemEnvironment",      "properties": {        "USER": {          "value": "reflectoring",          "origin": "System Environment Property \"USER\""        },        "HOME": {          "value": "/Users/reflectoring",          "origin": "System Environment Property \"HOME\""        }        ... other environment variables omitted ...      }    },    {      "name": "Config resource 'class path resource [application.properties]' via location 'optional:classpath:/'",      "properties": {        "management.endpoint.logfile.enabled": {          "value": "true",          "origin": "class path resource [application.properties] - 2:37"        },        "management.endpoints.web.exposure.include": {          "value": "metrics,beans,mappings,startup,env, info,loggers",          "origin": "class path resource [application.properties] - 5:43"        }      }    }  ]}

Использование конечной точкиscheduledtasks

Эта конечная точка позволяет нам проверять, выполняет ли приложение какую-либо задачу периодически, используя@Scheduled аннотациюSpring:

$ curl http://localhost:8080/actuator/scheduledtasks | jq{  "cron": [    {      "runnable": {        "target": "io.reflectoring.springboot.actuator.services.tasks.ReportGenerator.generateReports"      },      "expression": "0 0 12 * * *"    }  ],  "fixedDelay": [    {      "runnable": {        "target": "io.reflectoring.springboot.actuator.services.tasks.CleanUpAbandonedBaskets.process"      },      "initialDelay": 0,      "interval": 900000    }  ],  "fixedRate": [],  "custom": []}

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

Использование конечной точкиcaches

Эта конечная точка перечисляет все кэши приложений:

$ curl http://localhost:8080/actuator/caches | jq{  "cacheManagers": {    "cacheManager": {      "caches": {        "states": {          "target": "java.util.concurrent.ConcurrentHashMap"        },        "shippingPrice": {          "target": "java.util.concurrent.ConcurrentHashMap"        }      }    }  }}

Мы можем сказать,что приложение кэширует некоторые данные: states и shippingPrice. Это дает нам еще одну область приложения, которую нужно изучить и узнать больше: как создаются кеши, когда удаляются записи кеша и т. д.

Использование конечной точкиhealth

Конечная точка health показывает информацию оздоровье приложения:

$ curl http://localhost:8080/actuator/health{"status":"UP"}

Обычно это неглубокая проверка здоровья.Хотя это полезно в производственной среде для частой проверки балансировщиком нагрузки, это не помогает нам в понимании приложения.

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

Прочтитеэтустатьюна Reflectoring,чтобы узнать больше о реализации проверок работоспособности с помощью Actuator.

Использование конечной точкиmetrics

Эта конечная точка перечисляет все метрики, сгенерированные приложением:

$ curl http://localhost:8080/actuator/metrics | jq{  "names": [    "http.server.requests",    "jvm.buffer.count",    "jvm.buffer.memory.used",    "jvm.buffer.total.capacity",    "jvm.threads.states",    "logback.events",    "orders.placed.counter",    "process.cpu.usage",    ... other metrics omitted ...  ]}

Затем мы можем получить данные отдельных показателей:

$ curl http://localhost:8080/actuator/metrics/jvm.memory.used | jq{  "name": "jvm.memory.used",  "description": "The amount of used memory",  "baseUnit": "bytes",  "measurements": [    {      "statistic": "VALUE",      "value": 148044128    }  ],  "availableTags": [    {      "tag": "area",      "values": [        "heap",        "nonheap"      ]    },    {      "tag": "id",      "values": [        "CodeHeap 'profiled nmethods'",        "G1 Old Gen",                ... other tags omitted ...      ]    }  ]}

Особенно полезна проверка доступных пользовательских метрик API.Это может дать нам некоторое представление о том, что важно в этом приложении с точки зрения бизнеса.Например, из списка показателей мы можем видеть, что есть индикатор, orders.placed.counter который, вероятно, сообщает нам, сколько заказов было размещено за определенный период времени.

Заключение

В этой статье мы узнали, как использовать Spring Actuator в нашей локальной среде разработки для изучения нового приложения.Мы рассмотрели несколько конечных точек исполнительных механизмов, которые могут помочь нам определить важные области кодовой базы, которые могут потребовать более глубокого изучения.Попутно мы также узнали, как обрабатывать JSON в командной строке, используя легкий и чрезвычайно мощный инструментjq.

Вы можете поиграть с полным приложением, иллюстрирующим эти идеи, используя кодна GitHub.

Источник: habr.com
К списку статей
Опубликовано: 10.06.2021 10:07:14
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Java

Actuator

Spring boot

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru