1. Введение
Micrometer обеспечивает простую видимость поверх клиентов измерительных приборов для ряда популярных систем мониторинга. В настоящее время он поддерживает следующие системы мониторинга: Atlas, Datadog, Graphite, Ganglia, Influx, JMX и Prometheus.
В этом руководстве мы познакомим вас с базовым использованием Micrometer и его интеграцией со Spring.
Для простоты мы возьмем Micrometer Atlas в качестве примера, чтобы продемонстрировать большинство наших вариантов использования.
2. Зависимость от Maven
Для начала добавим в pom.xml
следующую зависимость :
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-atlas</artifactId>
<version>1.7.1</version>
</dependency>
Последнюю версию можно найти здесь .
3. МетрРеестр
В Micrometer MeterRegistry
является основным компонентом, используемым для регистрации счетчиков. Мы можем выполнить итерацию по реестру и далее по метрикам каждого счетчика, чтобы сгенерировать временной ряд в серверной части с комбинациями метрик и их значений измерений.
Самая простая форма реестра — SimpleMeterRegistry
. Но в большинстве случаев мы должны использовать MeterRegistry
, специально разработанный для нашей системы мониторинга; для Атласа это AtlasMeterRegistry
.
CompositeMeterRegistry
позволяет добавлять несколько реестров. Он предоставляет решение для одновременной публикации метрик приложений в различных поддерживаемых системах мониторинга.
Мы можем добавить любой MeterRegistry
, необходимый для загрузки данных на несколько платформ:
CompositeMeterRegistry compositeRegistry = new CompositeMeterRegistry();
SimpleMeterRegistry oneSimpleMeter = new SimpleMeterRegistry();
AtlasMeterRegistry atlasMeterRegistry
= new AtlasMeterRegistry(atlasConfig, Clock.SYSTEM);
compositeRegistry.add(oneSimpleMeter);
compositeRegistry.add(atlasMeterRegistry);
В Micrometer есть поддержка статического глобального реестра, Metrics.globalRegistry
. Также предоставляется набор статических построителей на основе этого глобального реестра для генерации счетчиков в Метриках
:
@Test
public void givenGlobalRegistry_whenIncrementAnywhere_thenCounted() {
class CountedObject {
private CountedObject() {
Metrics.counter("objects.instance").increment(1.0);
}
}
Metrics.addRegistry(new SimpleMeterRegistry());
Metrics.counter("objects.instance").increment();
new CountedObject();
Optional<Counter> counterOptional = Optional.ofNullable(Metrics.globalRegistry
.find("objects.instance").counter());
assertTrue(counterOptional.isPresent());
assertTrue(counterOptional.get().count() == 2.0);
}
4. Теги
и счетчики
4.1. Теги
Идентификатор счетчика
состоит из имени и тегов. Мы должны следовать соглашению об именах, которое разделяет слова точкой, чтобы гарантировать переносимость имен метрик между несколькими системами мониторинга.
Counter counter = registry.counter("page.visitors", "age", "20s");
Теги
можно использовать для нарезки метрики для рассуждений о значениях. В приведенном выше коде page.visitors
— это имя счетчика с тегом age=20s .
В этом случае счетчик считает посетителей страницы в возрасте от 20 до 30 лет.
Для большой системы мы можем добавить общие теги в реестр. Например, предположим, что показатели относятся к определенному региону:
registry.config().commonTags("region", "ua-east");
4.2. Прилавок
Счетчик просто
сообщает количество по указанному свойству приложения. Мы можем создать собственный счетчик с помощью построителя Fluent или вспомогательного метода любого MetricRegistry
:
Counter counter = Counter
.builder("instance")
.description("indicates instance count of the object")
.tags("dev", "performance")
.register(registry);
counter.increment(2.0);
assertTrue(counter.count() == 2);
counter.increment(-1);
assertTrue(counter.count() == 1);
Как видно из фрагмента выше, мы попытались уменьшить счетчик на единицу, но можем монотонно увеличивать счетчик только на фиксированную положительную величину.
4.3. Таймеры
Чтобы измерить задержки или частоту событий в нашей системе, мы можем использовать Timers
. Таймер сообщит
, по крайней мере, общее время и количество событий определенного временного ряда.
Например, мы можем записать событие приложения, которое может длиться несколько секунд:
SimpleMeterRegistry registry = new SimpleMeterRegistry();
Timer timer = registry.timer("app.event");
timer.record(() -> {
try {
TimeUnit.MILLISECONDS.sleep(15);
} catch (InterruptedException ignored) {
}
});
timer.record(30, TimeUnit.MILLISECONDS);
assertTrue(2 == timer.count());
assertThat(timer.totalTime(TimeUnit.MILLISECONDS)).isBetween(40.0, 55.0);
Для записи длительных событий мы используем LongTaskTimer
:
SimpleMeterRegistry registry = new SimpleMeterRegistry();
LongTaskTimer longTaskTimer = LongTaskTimer
.builder("3rdPartyService")
.register(registry);
LongTaskTimer.Sample currentTaskId = longTaskTimer.start();
try {
TimeUnit.MILLISECONDS.sleep(2);
} catch (InterruptedException ignored) { }
long timeElapsed = currentTaskId.stop();
assertEquals(2L, timeElapsed/((int) 1e6),1L);
4.4. Измерять
Манометр показывает текущее значение счетчика.
В отличие от других счетчиков датчики
должны сообщать данные только при наблюдении. Датчики
могут быть полезны при мониторинге статистики кеша или коллекций:
SimpleMeterRegistry registry = new SimpleMeterRegistry();
List<String> list = new ArrayList<>(4);
Gauge gauge = Gauge
.builder("cache.size", list, List::size)
.register(registry);
assertTrue(gauge.value() == 0.0);
list.add("1");
assertTrue(gauge.value() == 1.0);
4.5. DistributionСводка
Распределение событий и простое резюме обеспечивает DistributionSummary
:
SimpleMeterRegistry registry = new SimpleMeterRegistry();
DistributionSummary distributionSummary = DistributionSummary
.builder("request.size")
.baseUnit("bytes")
.register(registry);
distributionSummary.record(3);
distributionSummary.record(4);
distributionSummary.record(5);
assertTrue(3 == distributionSummary.count());
assertTrue(12 == distributionSummary.totalAmount());
Более того, DistributionSummary
и Timers
можно обогащать процентилями:
SimpleMeterRegistry registry = new SimpleMeterRegistry();
Timer timer = Timer
.builder("test.timer")
.publishPercentiles(0.3, 0.5, 0.95)
.publishPercentileHistogram()
.register(registry);
Теперь во фрагменте выше в реестре будут доступны три датчика с тегами percentile=0.3
, centile
=0.5
и percentile
=0.95
, указывающие значения, ниже которых падает 95%, 50% и 30% наблюдений соответственно. .
Итак, чтобы увидеть эти процентили в действии, давайте добавим несколько записей:
timer.record(2, TimeUnit.SECONDS);
timer.record(2, TimeUnit.SECONDS);
timer.record(3, TimeUnit.SECONDS);
timer.record(4, TimeUnit.SECONDS);
timer.record(8, TimeUnit.SECONDS);
timer.record(13, TimeUnit.SECONDS);
Затем мы можем проверить, извлекая значения из этих трех процентильных датчиков
:
Map<Double, Double> actualMicrometer = new TreeMap<>();
ValueAtPercentile[] percentiles = timer.takeSnapshot().percentileValues();
for (ValueAtPercentile percentile : percentiles) {
actualMicrometer.put(percentile.percentile(), percentile.value(TimeUnit.MILLISECONDS));
}
Map<Double, Double> expectedMicrometer = new TreeMap<>();
expectedMicrometer.put(0.3, 1946.157056);
expectedMicrometer.put(0.5, 3019.89888);
expectedMicrometer.put(0.95, 13354.663936);
assertEquals(expectedMicrometer, actualMicrometer);
Кроме того, Micrometer также поддерживает цель уровня обслуживания (гистограмму):
DistributionSummary hist = DistributionSummary
.builder("summary")
.serviceLevelObjectives(1, 10, 5)
.register(registry);
Как и в случае с процентилями, после добавления нескольких записей мы видим, что гистограмма довольно хорошо справляется с вычислениями:
Map<Integer, Double> actualMicrometer = new TreeMap<>();
HistogramSnapshot snapshot = hist.takeSnapshot();
Arrays.stream(snapshot.histogramCounts()).forEach(p -> {
actualMicrometer.put((Integer.valueOf((int) p.bucket())), p.count());
});
Map<Integer, Double> expectedMicrometer = new TreeMap<>();
expectedMicrometer.put(1,0D);
expectedMicrometer.put(10,2D);
expectedMicrometer.put(5,1D);
assertEquals(expectedMicrometer, actualMicrometer);
Как правило, гистограммы могут помочь проиллюстрировать прямое сравнение в отдельных сегментах. Гистограммы также можно масштабировать по времени, что весьма полезно для анализа времени отклика серверной службы:
Duration[] durations = {Duration.ofMillis(25), Duration.ofMillis(300), Duration.ofMillis(600)};
Timer timer = Timer
.builder("timer")
.sla(durations)
.publishPercentileHistogram()
.register(registry);
5. Связующие
Micrometer имеет несколько встроенных привязок для мониторинга JVM, кешей, ExecutorService
и служб ведения журналов.
Когда дело доходит до мониторинга JVM и системы, мы можем отслеживать метрики загрузчика классов ( ClassLoaderMetrics
), пул памяти JVM ( JvmMemoryMetrics
) и метрики GC ( JvmGcMetrics
), а также использование потоков и ЦП ( JvmThreadMetrics
, ProcessorMetrics
).
Мониторинг кэша (в настоящее время поддерживаются только Guava, EhCache, Hazelcast и Caffeine) поддерживается с помощью инструментов GuavaCacheMetrics
, EhCache2Metrics
, HazelcastCacheMetrics
и CaffeineCacheMetrics
. А для мониторинга службы возврата журналов мы можем привязать LogbackMetrics
к любому допустимому реестру:
new LogbackMetrics().bind(registry);
Использование вышеуказанных связывателей очень похоже на LogbackMetrics,
и все они довольно просты, поэтому мы не будем вдаваться в подробности здесь.
6. Весенняя интеграция
Spring Boot Actuator обеспечивает управление зависимостями и автоматическую настройку для Micrometer. Теперь он поддерживается в Spring Boot 2.0/1.x и Spring Framework 5.0/4.x.
Нам понадобится следующая зависимость (последнюю версию можно найти здесь ):
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-spring-legacy</artifactId>
<version>1.3.20</version>
</dependency>
Без каких-либо дополнительных изменений в существующем коде мы включили поддержку Spring с помощью Micrometer. Метрики памяти JVM нашего приложения Spring будут автоматически зарегистрированы в глобальном реестре и опубликованы в конечной точке атласа по умолчанию: http://localhost:7101/api/v1/publish
.
Существует несколько настраиваемых свойств для управления поведением при экспорте метрик, начиная с spring.metrics.atlas.*
. Проверьте AtlasConfig
, чтобы увидеть полный список свойств конфигурации для публикации Atlas.
Если нам нужно связать больше метрик, добавьте их только как @Bean
в контекст приложения.
Скажем, нам нужен JvmThreadMetrics
:
@Bean
JvmThreadMetrics threadMetrics(){
return new JvmThreadMetrics();
}
Что касается веб-мониторинга, он автоматически настраивается для каждой конечной точки в нашем приложении, но им можно управлять с помощью свойства конфигурации spring.metrics.web.autoTimeServerRequests
.
Реализация по умолчанию предоставляет четыре измерения метрик для конечных точек: метод запроса HTTP, код ответа HTTP, URI конечной точки и информацию об исключении.
При ответе на запросы метрики, относящиеся к методу запроса ( GET
, POST
и т. д .), будут опубликованы в Atlas.
С помощью Atlas Graph API мы можем создать график для сравнения времени отклика для разных методов:
По умолчанию также будут сообщены коды ответов 20x
, 30x
, 40x
, 50x :
Мы также можем сравнить разные URI:
Или проверьте метрики исключений:
Обратите внимание, что мы также можем использовать @Timed
в классе контроллера или конкретных методах конечной точки для настройки тегов, длительной задачи, квантилей и процентилей показателей:
@RestController
@Timed("people")
public class PeopleController {
@GetMapping("/people")
@Timed(value = "people.all", longTask = true)
public List<String> listPeople() {
//...
}
}
Основываясь на приведенном выше коде, мы можем увидеть следующие теги, проверив конечную точку Atlas http://localhost:7101/api/v1/tags/name
:
["people", "people.all", "jvmBufferCount", ... ]
Micrometer также работает в функциональной веб-инфраструктуре, представленной в Spring Boot 2.0. Мы можем включить метрики, отфильтровав RouterFunction
:
RouterFunctionMetrics metrics = new RouterFunctionMetrics(registry);
RouterFunctions.route(...)
.filter(metrics.timer("server.requests"));
Мы также можем собирать метрики из источника данных и запланированных задач. Проверьте официальную документацию для более подробной информации.
7. Заключение
В этой статье мы представили метрику фасада Micrometer. Абстрагируясь и поддерживая несколько систем мониторинга с общей семантикой, инструмент позволяет легко переключаться между различными платформами мониторинга.
Как всегда, полный код реализации этой статьи можно найти на Github .