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

Введение в метрики Dropwizard

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

1. Введение

Metrics — это библиотека Java, которая предоставляет измерительные инструменты для приложений Java.

Он имеет несколько модулей, и в этой статье мы подробно разработаем модуль metrics-core, модуль metrics-healthchecks, модуль metrics-servlets и модуль metrics-servlet, а также набросаем остальные для справки.

2. Метрики модуля-ядро

2.1. Зависимости Maven

Для использования модуля metrics-core требуется только одна зависимость, которую необходимо добавить в файл pom.xml :

<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-core</artifactId>
<version>3.1.2</version>
</dependency>

И вы можете найти его последнюю версию здесь .

2.2. МетрикРеестр

Проще говоря, мы будем использовать класс MetricRegistry для регистрации одной или нескольких метрик.

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

Теперь создадим MetricRegistry :

MetricRegistry metricRegistry = new MetricRegistry();

И тогда мы можем зарегистрировать некоторые метрики с помощью этого MetricRegistry :

Meter meter1 = new Meter();
metricRegistry.register("meter1", meter1);

Meter meter2 = metricRegistry.meter("meter2");

Существует два основных способа создания новой метрики: создать экземпляр самостоятельно или получить его из реестра метрик. Как вы можете видеть, мы использовали их оба в приведенном выше примере, мы создаем экземпляр объекта Meter «meter1» и получаем другой объект Meter «meter2» , созданный metricRegistry .

В реестре метрик каждая метрика имеет уникальное имя, так как мы использовали «meter1» и «meter2» в качестве имен метрик выше. MetricRegistry также предоставляет набор статических вспомогательных методов, помогающих нам создавать правильные имена метрик:

String name1 = MetricRegistry.name(Filter.class, "request", "count");
String name2 = MetricRegistry.name("CustomFilter", "response", "count");

Если нам нужно управлять набором реестров метрик, мы можем использовать класс SharedMetricRegistries , который является одноэлементным и потокобезопасным. Мы можем добавить в него метрический регистр, извлечь из него этот метрический регистр и удалить его:

SharedMetricRegistries.add("default", metricRegistry);
MetricRegistry retrievedMetricRegistry = SharedMetricRegistries.getOrCreate("default");
SharedMetricRegistries.remove("default");

3. Понятия метрик

Модуль metrics-core предоставляет несколько часто используемых типов метрик: Meter , Gauge , Counter , Histogram и Timer , а также Reporter для вывода значений метрик .

3.1. метр

Счетчик измеряет количество и частоту возникновения событий:

Meter meter = new Meter();
long initCount = meter.getCount();
assertThat(initCount, equalTo(0L));

meter.mark();
assertThat(meter.getCount(), equalTo(1L));

meter.mark(20);
assertThat(meter.getCount(), equalTo(21L));

double meanRate = meter.getMeanRate();
double oneMinRate = meter.getOneMinuteRate();
double fiveMinRate = meter.getFiveMinuteRate();
double fifteenMinRate = meter.getFifteenMinuteRate();

Метод getCount() возвращает количество событий, а метод mark() добавляет 1 или n к количеству событий. Объект Meter предоставляет четыре скорости, которые представляют собой средние скорости за весь срок службы Meter , за последнюю минуту, за последние пять минут и за последний квартал соответственно.

3.2. Измерять

Датчик — это интерфейс, который просто используется для возврата определенного значения. Модуль metrics-core предоставляет несколько его реализаций: RatioGauge , CachedGauge , DerivativeGauge и JmxAttributeGauge .

RatioGauge — это абстрактный класс, измеряющий отношение одного значения к другому.

Давайте посмотрим, как его использовать. Во-первых, мы реализуем класс AttendanceRatioGauge :

public class AttendanceRatioGauge extends RatioGauge {
private int attendanceCount;
private int courseCount;

@Override
protected Ratio getRatio() {
return Ratio.of(attendanceCount, courseCount);
}

// standard constructors
}

А потом тестируем:

RatioGauge ratioGauge = new AttendanceRatioGauge(15, 20);

assertThat(ratioGauge.getValue(), equalTo(0.75));

CachedGauge — это еще один абстрактный класс, который может кэшировать значения, поэтому он весьма полезен, когда вычисление значений требует больших затрат. Чтобы использовать его, нам нужно реализовать класс ActiveUsersGauge :

public class ActiveUsersGauge extends CachedGauge<List<Long>> {

@Override
protected List<Long> loadValue() {
return getActiveUserCount();
}

private List<Long> getActiveUserCount() {
List<Long> result = new ArrayList<Long>();
result.add(12L);
return result;
}

// standard constructors
}

Затем мы тестируем его, чтобы увидеть, работает ли он так, как ожидалось:

Gauge<List<Long>> activeUsersGauge = new ActiveUsersGauge(15, TimeUnit.MINUTES);
List<Long> expected = new ArrayList<>();
expected.add(12L);

assertThat(activeUsersGauge.getValue(), equalTo(expected));

Мы устанавливаем время истечения кеша на 15 минут при создании экземпляра ActiveUsersGauge .

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

Давайте посмотрим на пример:

public class ActiveUserCountGauge extends DerivativeGauge<List<Long>, Integer> {

@Override
protected Integer transform(List<Long> value) {
return value.size();
}

// standard constructors
}

Этот датчик получает свое значение от ActiveUsersGauge , поэтому мы ожидаем, что это будет значение размера базового списка:

Gauge<List<Long>> activeUsersGauge = new ActiveUsersGauge(15, TimeUnit.MINUTES);
Gauge<Integer> activeUserCountGauge = new ActiveUserCountGauge(activeUsersGauge);

assertThat(activeUserCountGauge.getValue(), equalTo(1));

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

3.3. Прилавок

Счетчик используется для записи приращений и уменьшений:

Counter counter = new Counter();
long initCount = counter.getCount();
assertThat(initCount, equalTo(0L));

counter.inc();
assertThat(counter.getCount(), equalTo(1L));

counter.inc(11);
assertThat(counter.getCount(), equalTo(12L));

counter.dec();
assertThat(counter.getCount(), equalTo(11L));

counter.dec(6);
assertThat(counter.getCount(), equalTo(5L));

3.4. Гистограмма

Гистограмма используется для отслеживания потока длинных значений и анализа их статистических характеристик, таких как максимальное, минимальное, среднее значение, медиана, стандартное отклонение, 75-й процентиль и т. д.:

Histogram histogram = new Histogram(new UniformReservoir());
histogram.update(5);
long count1 = histogram.getCount();
assertThat(count1, equalTo(1L));

Snapshot snapshot1 = histogram.getSnapshot();
assertThat(snapshot1.getValues().length, equalTo(1));
assertThat(snapshot1.getValues()[0], equalTo(5L));

histogram.update(20);
long count2 = histogram.getCount();
assertThat(count2, equalTo(2L));

Snapshot snapshot2 = histogram.getSnapshot();
assertThat(snapshot2.getValues().length, equalTo(2));
assertThat(snapshot2.getValues()[1], equalTo(20L));
assertThat(snapshot2.getMax(), equalTo(20L));
assertThat(snapshot2.getMean(), equalTo(12.5));
assertEquals(10.6, snapshot2.getStdDev(), 0.1);
assertThat(snapshot2.get75thPercentile(), equalTo(20.0));
assertThat(snapshot2.get999thPercentile(), equalTo(20.0));

Гистограмма производит выборку данных, используя выборку резервуара, и когда мы создаем экземпляр объекта гистограммы , нам необходимо явно указать его резервуар.

Reservoir — это интерфейс, и metrics-core предоставляет четыре их реализации: ExponentiallyDecayingReservoir , UniformReservoir , SlidingTimeWindowReservoir , SlidingWindowReservoir .

В разделе выше мы упомянули, что метрика также может быть создана MetricRegistry, помимо использования конструктора . Когда мы используем metricRegistry.histogram() , он возвращает экземпляр Histogram с реализацией ExponentiallyDecayingReservoir .

3.5. Таймер

Таймер используется для отслеживания нескольких временных промежутков, которые представлены объектами Context , а также предоставляет их статистические данные:

Timer timer = new Timer();
Timer.Context context1 = timer.time();
TimeUnit.SECONDS.sleep(5);
long elapsed1 = context1.stop();

assertEquals(5000000000L, elapsed1, 1000000);
assertThat(timer.getCount(), equalTo(1L));
assertEquals(0.2, timer.getMeanRate(), 0.1);

Timer.Context context2 = timer.time();
TimeUnit.SECONDS.sleep(2);
context2.close();

assertThat(timer.getCount(), equalTo(2L));
assertEquals(0.3, timer.getMeanRate(), 0.1);

3.6. Репортер

Когда нам нужно вывести наши измерения, мы можем использовать Reporter . Это интерфейс, и модуль metrics-core предоставляет несколько его реализаций, таких как ConsoleReporter , CsvReporter , Slf4jReporter , JmxReporter и так далее.

Здесь мы используем ConsoleReporter в качестве примера:

MetricRegistry metricRegistry = new MetricRegistry();

Meter meter = metricRegistry.meter("meter");
meter.mark();
meter.mark(200);
Histogram histogram = metricRegistry.histogram("histogram");
histogram.update(12);
histogram.update(17);
Counter counter = metricRegistry.counter("counter");
counter.inc();
counter.dec();

ConsoleReporter reporter = ConsoleReporter.forRegistry(metricRegistry).build();
reporter.start(5, TimeUnit.MICROSECONDS);
reporter.report();

Вот пример вывода ConsoleReporter:

-- Histograms ------------------------------------------------------------------
histogram
count = 2
min = 12
max = 17
mean = 14.50
stddev = 2.50
median = 17.00
75% <= 17.00
95% <= 17.00
98% <= 17.00
99% <= 17.00
99.9% <= 17.00

-- Meters ----------------------------------------------------------------------
meter
count = 201
mean rate = 1756.87 events/second
1-minute rate = 0.00 events/second
5-minute rate = 0.00 events/second
15-minute rate = 0.00 events/second

4. Метрики модулей-проверки работоспособности

В Metrics есть модуль расширения metrics-healthchecks для работы с проверками работоспособности.

4.1. Зависимости Maven

Чтобы использовать модуль metrics-healthchecks, нам нужно добавить эту зависимость в файл pom.xml :

<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-healthchecks</artifactId>
<version>3.1.2</version>
</dependency>

И вы можете найти его последнюю версию здесь .

4.2. Применение

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

Например, мы используем DatabaseHealthCheck и UserCenterHealthCheck :

public class DatabaseHealthCheck extends HealthCheck {

@Override
protected Result check() throws Exception {
return Result.healthy();
}
}
public class UserCenterHealthCheck extends HealthCheck {

@Override
protected Result check() throws Exception {
return Result.healthy();
}
}

Затем нам понадобится HealthCheckRegistry (который похож на MetricRegistry ) и зарегистрируйте в нем DatabaseHealthCheck и UserCenterHealthCheck :

HealthCheckRegistry healthCheckRegistry = new HealthCheckRegistry();
healthCheckRegistry.register("db", new DatabaseHealthCheck());
healthCheckRegistry.register("uc", new UserCenterHealthCheck());

assertThat(healthCheckRegistry.getNames().size(), equalTo(2));

Мы также можем отменить регистрацию HealthCheck :

healthCheckRegistry.unregister("uc");

assertThat(healthCheckRegistry.getNames().size(), equalTo(1));

Мы можем запустить все экземпляры HealthCheck :

Map<String, HealthCheck.Result> results = healthCheckRegistry.runHealthChecks();
for (Map.Entry<String, HealthCheck.Result> entry : results.entrySet()) {
assertThat(entry.getValue().isHealthy(), equalTo(true));
}

Наконец, мы можем запустить конкретный экземпляр HealthCheck :

healthCheckRegistry.runHealthCheck("db");

5. Модуль метрики-сервлеты

Метрики предоставляют нам несколько полезных сервлетов, которые позволяют нам получать доступ к данным, связанным с метриками, через HTTP-запросы.

5.1. Зависимости Maven

Чтобы использовать модуль metrics-servlets, нам нужно добавить эту зависимость в файл pom.xml :

<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-servlets</artifactId>
<version>3.1.2</version>
</dependency>

И вы можете найти его последнюю версию здесь .

5.2. Использование HealthCheckServlet

HealthCheckServlet предоставляет результаты проверки работоспособности. Во-первых, нам нужно создать ServletContextListener , который предоставляет наш HealthCheckRegistry :

public class MyHealthCheckServletContextListener
extends HealthCheckServlet.ContextListener {

public static HealthCheckRegistry HEALTH_CHECK_REGISTRY
= new HealthCheckRegistry();

static {
HEALTH_CHECK_REGISTRY.register("db", new DatabaseHealthCheck());
}

@Override
protected HealthCheckRegistry getHealthCheckRegistry() {
return HEALTH_CHECK_REGISTRY;
}
}

Затем мы добавляем этот прослушиватель и HealthCheckServlet в файл web.xml :

<listener>
<listener-class>com.foreach.metrics.servlets.MyHealthCheckServletContextListener</listener-class>
</listener>
<servlet>
<servlet-name>healthCheck</servlet-name>
<servlet-class>com.codahale.metrics.servlets.HealthCheckServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>healthCheck</servlet-name>
<url-pattern>/healthcheck</url-pattern>
</servlet-mapping>

Теперь мы можем запустить веб-приложение и отправить запрос GET на «http://localhost:8080/healthcheck», чтобы получить результаты проверки работоспособности. Его ответ должен быть таким:

{
"db": {
"healthy": true
}
}

5.3. Использование ThreadDumpServlet

ThreadDumpServlet предоставляет информацию обо всех живых потоках в JVM, их состояниях, их трассировках стека и состоянии любых блокировок, которых они могут ожидать.

Если мы хотим использовать его, нам просто нужно добавить их в файл web.xml :

<servlet>
<servlet-name>threadDump</servlet-name>
<servlet-class>com.codahale.metrics.servlets.ThreadDumpServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>threadDump</servlet-name>
<url-pattern>/threaddump</url-pattern>
</servlet-mapping>

Данные дампа потока будут доступны по адресу «http://localhost:8080/threaddump».

5.4. Использование PingServlet

PingServlet можно использовать для проверки работы приложения. Мы добавляем их в файл web.xml :

<servlet>
<servlet-name>ping</servlet-name>
<servlet-class>com.codahale.metrics.servlets.PingServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ping</servlet-name>
<url-pattern>/ping</url-pattern>
</servlet-mapping>

А затем отправьте запрос GET на «http://localhost:8080/ping». Код состояния ответа — 200, а его содержание — «pong».

5.5. Метрики Использование сервлета

MetricsServlet предоставляет данные метрик. Во-первых, нам нужно создать ServletContextListener , который предоставляет наш MetricRegistry :

public class MyMetricsServletContextListener
extends MetricsServlet.ContextListener {
private static MetricRegistry METRIC_REGISTRY
= new MetricRegistry();

static {
Counter counter = METRIC_REGISTRY.counter("m01-counter");
counter.inc();

Histogram histogram = METRIC_REGISTRY.histogram("m02-histogram");
histogram.update(5);
histogram.update(20);
histogram.update(100);
}

@Override
protected MetricRegistry getMetricRegistry() {
return METRIC_REGISTRY;
}
}

И этот слушатель, и MetricsServlet необходимо добавить в web.xml :

<listener>
<listener-class>com.codahale.metrics.servlets.MyMetricsServletContextListener</listener-class>
</listener>
<servlet>
<servlet-name>metrics</servlet-name>
<servlet-class>com.codahale.metrics.servlets.MetricsServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>metrics</servlet-name>
<url-pattern>/metrics</url-pattern>
</servlet-mapping>

Это будет отображаться в нашем веб-приложении по адресу «http://localhost:8080/metrics». Его ответ должен содержать различные данные метрик:

{
"version": "3.0.0",
"gauges": {},
"counters": {
"m01-counter": {
"count": 1
}
},
"histograms": {
"m02-histogram": {
"count": 3,
"max": 100,
"mean": 41.66666666666666,
"min": 5,
"p50": 20,
"p75": 100,
"p95": 100,
"p98": 100,
"p99": 100,
"p999": 100,
"stddev": 41.69998667732268
}
},
"meters": {},
"timers": {}
}

5.6. Использование AdminServlet

AdminServlet объединяет HealthCheckServlet , ThreadDumpServlet , MetricsServlet и PingServlet .

Давайте добавим их в web.xml :

<servlet>
<servlet-name>admin</servlet-name>
<servlet-class>com.codahale.metrics.servlets.AdminServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>admin</servlet-name>
<url-pattern>/admin/*</url-pattern>
</servlet-mapping>

Теперь к нему можно получить доступ по адресу «http://localhost:8080/admin». Мы получим страницу, содержащую четыре ссылки, по одной на каждый из этих четырех сервлетов.

Обратите внимание, что если мы хотим выполнить проверку работоспособности и получить доступ к данным метрик, эти два прослушивателя все еще необходимы.

6. Модуль метрики-сервлет

Модуль metrics-servlet предоставляет фильтр , который имеет несколько показателей: счетчики для кодов состояния, счетчик для количества активных запросов и таймер для длительности запроса.

6.1. Зависимости Maven

Чтобы использовать этот модуль, давайте сначала добавим зависимость в pom.xml :

<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-servlet</artifactId>
<version>3.1.2</version>
</dependency>

И вы можете найти его последнюю версию здесь .

6.2. Применение

Чтобы использовать его, нам нужно создать ServletContextListener , который предоставляет наш MetricRegistry для InstrumentedFilter :

public class MyInstrumentedFilterContextListener
extends InstrumentedFilterContextListener {

public static MetricRegistry REGISTRY = new MetricRegistry();

@Override
protected MetricRegistry getMetricRegistry() {
return REGISTRY;
}
}

Затем мы добавляем их в web.xml :

<listener>
<listener-class>
com.foreach.metrics.servlet.MyInstrumentedFilterContextListener
</listener-class>
</listener>

<filter>
<filter-name>instrumentFilter</filter-name>
<filter-class>
com.codahale.metrics.servlet.InstrumentedFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>instrumentFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

Теперь InstrumentedFilter может работать. Если мы хотим получить доступ к данным его метрик, мы можем сделать это через его MetricRegistry REGISTRY .

7. Другие модули

Помимо модулей, которые мы представили выше, в Метрике есть несколько других модулей для разных целей:

  • metrics-jvm : предоставляет несколько полезных метрик для инструментирования внутренних компонентов JVM.
  • metrics-ehcache : предоставляет InstrumentedEhcache , декоратор для кешей Ehcache.
  • metrics-httpclient : предоставляет классы для инструментирования Apache HttpClient (версия 4.x)
  • metrics-log4j : предоставляет InstrumentedAppender , реализацию приложения Log4j Appender для log4j 1.x, которая записывает частоту регистрируемых событий по их уровню ведения журнала.
  • metrics-log4j2 : похож на metrics-log4j, только для log4j 2.x.
  • metrics-logback : предоставляет InstrumentedAppender , реализацию Logback Appender , которая записывает частоту зарегистрированных событий по их уровню ведения журнала.
  • metrics-json : предоставляет HealthCheckModule и MetricsModule для Джексона .

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

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

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

Как всегда, полный исходный код примера доступен на GitHub .