1. введение
Микрометр обеспечивает простой фасад по сравнению с клиентами-приборами для ряда популярных систем мониторинга. В настоящее время он поддерживает следующие системы мониторинга: Atlas, Datadog, Graphite, Ganglia, Influx, JMX и Prometheus.
В этой статье мы познакомим вас с основным использованием микрометра и его интеграцией с Spring.
Для простоты мы возьмем Микрометрический Атлас в качестве примера, чтобы продемонстрировать большинство наших вариантов использования.
2. Зависимость от Maven
Для начала давайте добавим следующую зависимость в pom.xml :
io.micrometer micrometer-registry-atlas 0.12.0.RELEASE
Последнюю версию можно найти здесь .
3. Реестр счетчиков
В микрометре реестр счетчиков является основным компонентом, используемым для регистрации счетчиков. Мы можем выполнить итерацию по реестру и далее по метрикам каждого счетчика, чтобы сгенерировать временные ряды в бэкэнде с комбинациями метрик и их значениями измерений.
Простейшей формой реестра является Простой реестр счетчиков . Но в большинстве случаев мы должны использовать реестр счетчиков , явно разработанный для нашей системы мониторинга; для Atlas это AtlasMeterRegistry .
Составной реестр счетчиков позволяет добавлять несколько реестров. Он предоставляет решение для одновременной публикации показателей приложений в различных поддерживаемых системах мониторинга.
Мы можем добавить любой Реестр счетчиков , необходимый для загрузки данных на несколько платформ:
CompositeMeterRegistry compositeRegistry = new CompositeMeterRegistry(); SimpleMeterRegistry oneSimpleMeter = new SimpleMeterRegistry(); AtlasMeterRegistry atlasMeterRegistry = new AtlasMeterRegistry(atlasConfig, Clock.SYSTEM); compositeRegistry.add(oneSimpleMeter); compositeRegistry.add(atlasMeterRegistry);
В микрометре есть статическая поддержка глобального реестра: Метрики.глобальный реестр . Кроме того, для создания счетчиков в Метриках предусмотрен набор статических построителей на основе этого глобального реестра:
@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(); OptionalcounterOptional = 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");
Теги можно использовать для нарезки метрики для рассуждений о значениях. В приведенном выше коде страница.посетители – это имя счетчика, с возрастом=20 лет в качестве тега. В этом случае счетчик предназначен для подсчета посетителей страницы в возрасте от 20 до 30 лет.
Для большой системы мы можем добавить общие теги в реестр, скажем, показатели из определенного региона:
registry.config().commonTags("region", "ua-east");
4.2. Счетчик
Счетчик сообщает только о количестве по указанному свойству приложения. Мы можем создать пользовательский счетчик с помощью fluent builder или вспомогательного метода любого 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() == 2);
Как видно из приведенного выше фрагмента, мы попытались уменьшить счетчик на единицу, но мы можем монотонно увеличивать счетчик только на фиксированную положительную величину.
4.3. Таймеры
Для измерения задержек или частоты событий в нашей системе мы можем использовать Таймеры . Таймер сообщит, по крайней мере, общее время и количество событий определенного временного ряда.
Например, мы можем записать событие приложения, которое может длиться несколько секунд:
SimpleMeterRegistry registry = new SimpleMeterRegistry(); Timer timer = registry.timer("app.event"); timer.record(() -> { try { TimeUnit.MILLISECONDS.sleep(1500); } catch (InterruptedException ignored) { } }); timer.record(3000, MILLISECONDS); assertTrue(2 == timer.count()); assertTrue(4510 > timer.totalTime(MILLISECONDS) && 4500 <= timer.totalTime(MILLISECONDS));
Для записи длительных событий мы используем Таймер длительных задач :
SimpleMeterRegistry registry = new SimpleMeterRegistry(); LongTaskTimer longTaskTimer = LongTaskTimer .builder("3rdPartyService") .register(registry); long currentTaskId = longTaskTimer.start(); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException ignored) { } long timeElapsed = longTaskTimer.stop(currentTaskId); assertTrue(timeElapsed / (int) 1e9 == 2);
4.4. Калибр
Датчик показывает текущее значение счетчика.
В отличие от других счетчиков, Датчики должны сообщать данные только при наблюдении. Датчики могут быть полезны при мониторинге состояния кэша, коллекций и т.д.:
SimpleMeterRegistry registry = new SimpleMeterRegistry(); Listlist = 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. Краткое описание распространения
Распределение событий и простая сводка предоставляются Сводка распространения :
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());
Кроме того, Сводка распределения и Таймеры могут быть обогащены квантилями:
SimpleMeterRegistry registry = new SimpleMeterRegistry(); Timer timer = Timer.builder("test.timer") .quantiles(WindowSketchQuantiles .quantiles(0.3, 0.5, 0.95) .create()) .register(registry);
В приведенном выше фрагменте в реестре будут доступны три датчика с тегами квантиль=0,3 , квантиль=0,5 и квантиль=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);
Затем мы можем проверить, извлекая значения в этих трех квантилях Измерителях :
ListquantileGauges = registry.getMeters().stream() .filter(m -> m.getType().name().equals("Gauge")) .map(meter -> (Gauge) meter) .collect(Collectors.toList()); assertTrue(3 == quantileGauges.size()); Map quantileMap = extractTagValueMap(registry, Type.Gauge, 1e9); assertThat(quantileMap, allOf( hasEntry("quantile=0.3",2), hasEntry("quantile=0.5", 3), hasEntry("quantile=0.95", 8)));
Кроме того, микрометр также поддерживает гистограммы:
DistributionSummary hist = DistributionSummary .builder("summary") .histogram(Histogram.linear(0, 10, 5)) .register(registry);
Подобно квантилям, после добавления нескольких записей мы видим, что гистограмма довольно хорошо справляется с вычислениями:
Maphistograms = extractTagValueMap(registry, Type.Counter, 1.0); assertThat(histograms, allOf( hasEntry("bucket=0.0", 0), hasEntry("bucket=10.0", 2), hasEntry("bucket=20.0", 2), hasEntry("bucket=30.0", 1), hasEntry("bucket=40.0", 1), hasEntry("bucket=Infinity", 0)));
Как правило, гистограммы могут помочь проиллюстрировать прямое сравнение в отдельных сегментах. Гистограммы также могут быть масштабированы по времени, что весьма полезно для анализа времени отклика серверной службы:
SimpleMeterRegistry registry = new SimpleMeterRegistry(); Timer timer = Timer .builder("timer") .histogram(Histogram.linearTime(TimeUnit.MILLISECONDS, 0, 200, 3)) .register(registry); //... assertThat(histograms, allOf( hasEntry("bucket=0.0", 0), hasEntry("bucket=2.0E8", 1), hasEntry("bucket=4.0E8", 1), hasEntry("bucket=Infinity", 3)));
5. Связующие вещества
Микрометр имеет несколько встроенных связующих устройств для мониторинга JVM, кэшей, ExecutorService и служб ведения журнала.
Когда дело доходит до JVM и мониторинга системы, мы можем отслеживать показатели загрузчика классов ( Показатели загрузчика классов ), пул памяти JVM ( JvmMemoryMetrics ) и показатели GC ( JvmGcMetrics ), загрузку потоков и процессоров ( JvmThreadMetrics , ProcessorMetrics ).
Мониторинг кэша (в настоящее время поддерживаются только Guava, EhCache, Hazelcast и Кофеин) поддерживается с помощью инструментов GuavaCacheMetrics , EhCache2Metrics , HazelcastCacheMetrics и CaffeineCacheMetrics . И для мониторинга службы обратного входа мы можем привязать Показатели обратного входа к любому действительному реестру:
new LogbackMetrics().bind(registry);
Использование вышеперечисленных привязок очень похоже на Показатели обратной связи и все они довольно просты, поэтому мы не будем вдаваться в дальнейшие подробности здесь.
6. Интеграция Пружин
Привод пружинного загрузчика обеспечивает управление зависимостями и автоматическую настройку микрометра. Теперь он поддерживается в Spring Boot 2.0/1.x и Spring Framework 5.0/4.x.
Нам понадобится следующая зависимость (последнюю версию можно найти здесь ):
io.micrometer micrometer-spring-legacy 0.12.0.RELEASE
Без каких-либо дальнейших изменений в существующем коде мы включили поддержку пружины с помощью микрометра. Показатели памяти JVM нашего приложения Spring будут автоматически зарегистрированы в глобальном реестре и опубликованы в конечной точке atlas по умолчанию: http://localhost:7101/api/v1/publish .
Существует несколько настраиваемых свойств, доступных для управления поведением экспорта метрик, начиная с spring.metrics.atlas.* . Проверьте Конфигурация Atlas чтобы просмотреть полный список свойств конфигурации для публикации Atlas.
Если нам нужно связать больше метрик, добавьте их только как @Bean в контекст приложения.
Скажем, нам нужны метрики потока Jvm :
@Bean JvmThreadMetrics threadMetrics(){ return new JvmThreadMetrics(); }
Что касается веб-мониторинга, он автоматически настраивается для каждой конечной точки в нашем приложении, но им можно управлять с помощью свойства конфигурации: spring.metrics.web.autoTimeServerRequests .
Реализация по умолчанию предоставляет четыре измерения показателей для конечных точек: метод HTTP-запроса, код ответа HTTP, URI конечной точки и информацию об исключениях.
Когда на запросы будут даны ответы, показатели, относящиеся к методу запроса ( GET , POST и т.д.), Будут опубликованы в Атласе.
С Atlas Graph API , мы можем сгенерировать график для сравнения времени отклика для разных методов:
По умолчанию также будут сообщены коды ответов 20x , 30x , 40x , 50x :
Мы также можем сравнить различные URI:
или проверьте показатели исключений:
Обратите внимание, что мы также можем использовать @Timed в классе контроллера или в конкретных методах конечной точки для настройки тегов, длинной задачи, квантилей и процентилей метрик:
@RestController @Timed("people") public class PeopleController { @GetMapping("/people") @Timed(value = "people.all", longTask = true) public ListlistPeople() { //... } }
Основываясь на приведенном выше коде, мы можем увидеть следующие теги, проверив конечную точку Atlas http://localhost:7101/api/v1/tags/name :
["people", "people.all", "jvmBufferCount", ... ]
Микрометр также работает в веб-фреймворке функций, представленном в Spring Boot 2.0. Метрики можно включить, отфильтровав функцию Маршрутизатора :
RouterFunctionMetrics metrics = new RouterFunctionMetrics(registry); RouterFunctions.route(...) .filter(metrics.timer("server.requests"));
Также можно собирать показатели из источника данных и запланированные задачи. Проверьте официальную документацию для получения более подробной информации.
7. Заключение
В этой статье мы представили метрики фасадного микрометра. Абстрагируясь и поддерживая несколько систем мониторинга в рамках общей семантики, инструмент довольно легко переключается между различными платформами мониторинга.
Как всегда, полный код реализации этой статьи можно найти на Github .