Рубрики
Без рубрики

Введение в SPF4J

Углубленный взгляд на SPF4J с использованием некоторых интересных практических сценариев.

Автор оригинала: Mohan Sundararaju.

1. Обзор

Тестирование производительности-это деятельность, которая часто подталкивается к конечным этапам цикла разработки программного обеспечения. Мы обычно полагаемся на профилировщики Java для устранения проблем с производительностью.

В этом уроке мы рассмотрим простую платформу производительности для Java (SPF4J). Он предоставляет нам API, которые могут быть добавлены в наш код. В результате мы можем сделать мониторинг производительности неотъемлемой частью нашего компонента .

2. Основные концепции сбора и визуализации метрик

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

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

2.1. Захват метрик

Во-первых, нам нужно решить, что нужно измерить. Нас интересует показатель загрузок/мин. Поэтому , мы измерим количество загрузок.

Во-вторых, как часто нам нужно проводить измерения? Давайте решим “раз в минуту”.

Наконец, как долго мы должны следить? Давайте решим “на один час”.

С этими правилами мы готовы провести эксперимент. Как только эксперимент закончится, мы сможем увидеть результаты:

Time	Cumulative Downloads	Downloads/min
----------------------------------------------
T       497                     0  
T+1     624                     127
T+2     676                     52
...     
T+14    19347                   17390
T+15    19427                   80
...  
T+22    27195                   7350
...  
T+41    41321                   11885
...   
T+60    43395                   40

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

2.2. Визуализация показателей

Давайте построим простой линейный график времени vs загрузок/мин .

График загрузок в минуту

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

Давайте изменим загрузки ось, чтобы использовать логарифмическую шкалу (основание 10) и построить логарифмический/линейный график.

Теперь мы действительно начинаем видеть более низкие значения. И они ближе к 100 (+/-). Обратите внимание, что на линейном графике указано среднее значение 703 так же как и пики.

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

  • среднее значение загрузок/мин составляет порядка 100 секунд

3. Мониторинг производительности вызова функции

Поняв, как захватить простую метрику и проанализировать ее из предыдущего примера, давайте теперь применим ее к простому методу Java — isPrimeNumber :

private static boolean isPrimeNumber(long number) {
    for (long i = 2; i <= number / 2; i++) {
        if (number % i == 0)
            return false;
    }
    return true;
}

Используя SPF4J, есть два способа сбора метрик. Давайте рассмотрим их в следующем разделе.

4. Настройка и настройка

4.1. Настройка Maven

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

Основная библиотека-это spf4j-core , которая предоставляет нам большинство необходимых функций.

Давайте добавим это как зависимость Maven:


    org.spf4j
    spf4j-core
    8.6.10

Существует более подходящая библиотека для мониторинга производительности- spf4j — aspects , которая использует AspectJ .

Мы рассмотрим это в нашем примере, поэтому давайте добавим и это:


    org.spf4j
    spf4j-aspects
    8.6.10

И, наконец, SPF4J также поставляется с простым пользовательским интерфейсом, который весьма полезен для визуализации данных, поэтому давайте также добавим spf4j-ui :


    org.spf4j
    spf4j-ui
    8.6.10

4.2. Конфигурация выходных файлов

Платформа SPF4J записывает данные в базу данных временных рядов (TSDB) и при необходимости может также записывать их в текстовый файл.

Давайте настроим их оба и установим системное свойство spf4j.perf.ms.config :

public static void initialize() {
  String tsDbFile = System.getProperty("user.dir") + File.separator + "spf4j-performance-monitoring.tsdb2";
  String tsTextFile = System.getProperty("user.dir") + File.separator + "spf4j-performance-monitoring.txt";
  LOGGER.info("\nTime Series DB (TSDB) : {}\nTime Series text file : {}", tsDbFile, tsTextFile);
  System.setProperty("spf4j.perf.ms.config", "[email protected]" + tsDbFile + "," + "[email protected]" + tsTextFile);
}

4.3. Регистраторы и источники

Основная возможность платформы SPF4J заключается в записи, агрегировании и сохранении метрик, чтобы при их анализе не требовалась постобработка. Это делается с помощью классов Measurement Recorder и MeasurementRecorderSource .

Эти два класса предоставляют два различных способа записи метрики. Ключевое отличие заключается в том, что Measurementrecordersource может быть вызван из любого места, в то время как MeasurementRecorderSource используется только с аннотациями.

Фреймворк предоставляет нам класс Recorder Factory для создания экземпляров классов источников recorder и recorder для различных типов агрегаций:

  • создать масштабируемый квантованный рекордер() и создать масштабируемый источник квантованного рекордера()
  • создать масштабируемый регистратор подсчета() и создать масштабируемый источник регистратора подсчета()
  • создать масштабируемый Min Max Avg Recorder() и создать масштабируемый источник Min Max Avg Recorder()
  • создать прямой рекордер() и создать источник прямого рекордера()

Для нашего примера давайте выберем масштабируемую квантованную агрегацию.

4.4. Создание регистратора

Во-первых, давайте создадим вспомогательный метод для создания экземпляра Регистратора измерений :

public static MeasurementRecorder getMeasurementRecorder(Object forWhat) {
    String unitOfMeasurement = "ms";
    int sampleTimeMillis = 1_000;
    int factor = 10;
    int lowerMagnitude = 0;
    int higherMagnitude = 4;
    int quantasPerMagnitude = 10;

    return RecorderFactory.createScalableQuantizedRecorder(
      forWhat, unitOfMeasurement, sampleTimeMillis, factor, lowerMagnitude, 
      higherMagnitude, quantasPerMagnitude);
}

Давайте рассмотрим различные настройки:

  • unitOfMeasurement – измеряемое значение единицы измерения – для сценария мониторинга производительности, как правило, это единица времени
  • sampleTimeMillis – период времени для проведения измерений – или, другими словами, как часто проводить измерения
  • коэффициент – основание логарифмической шкалы, используемой для построения измеренного значения
  • lowerMagnitude – минимальное значение по логарифмической шкале – для логарифмической базы 10, lowerMagnitude означает 10 для мощности
  • higherMagnitude – максимальное значение по логарифмической шкале – для логарифмической базы 10, higherMagnitude означает от 10 до мощности,000
  • Квантаспермагнитность – количество участков в пределах величины – если величина колеблется от 1000 до 10 000, то Квантаспермагнитность означает, что диапазон будет разделен на 10 поддиапазонов

Мы видим, что ценности могут быть изменены в соответствии с нашими потребностями. Таким образом, было бы неплохо создать отдельные экземпляры Measurement Recorder для разных измерений.

4.5. Создание источника

Далее, давайте создадим экземпляр MeasurementRecorderSource , используя другой вспомогательный метод:

public static final class RecorderSourceForIsPrimeNumber extends RecorderSourceInstance {
    public static final MeasurementRecorderSource INSTANCE;
    static {
        Object forWhat = App.class + " isPrimeNumber";
        String unitOfMeasurement = "ms";
        int sampleTimeMillis = 1_000;
        int factor = 10;
        int lowerMagnitude = 0;
        int higherMagnitude = 4;
        int quantasPerMagnitude = 10;
        INSTANCE = RecorderFactory.createScalableQuantizedRecorderSource(
          forWhat, unitOfMeasurement, sampleTimeMillis, factor, 
          lowerMagnitude, higherMagnitude, quantasPerMagnitude);
    }
}

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

4.6. Создание класса конфигурации

Давайте теперь создадим удобный класс Spf4jConfig и поместим в него все вышеперечисленные методы:

public class Spf4jConfig {
    public static void initialize() {
        //...
    }

    public static MeasurementRecorder getMeasurementRecorder(Object forWhat) {
        //...
    }

    public static final class RecorderSourceForIsPrimeNumber extends RecorderSourceInstance {
        //...
    }
}

4.7. Настройка aop.xml

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

Давайте создадим наш класс и аспект с помощью loadtimeweaver и поставим aop.xml в папке META-INF :


    
        
    
    
        
        
    

5. Использование Регистратора Измерений

Теперь давайте посмотрим, как использовать Регистратор измерений для записи показателей производительности нашей тестовой функции.

5.1. Запись показателей

Давайте сгенерируем 100 случайных чисел и вызовем метод простой проверки в цикле. Перед этим давайте вызовем наш класс Spf4jConfig , чтобы выполнить инициализацию и создать экземпляр класса Measure Recorder . Используя этот экземпляр, давайте вызовем метод record () , чтобы сохранить индивидуальное время, затраченное на 100 вызовов is Prime Number() :

Spf4jConfig.initialize();
MeasurementRecorder measurementRecorder = Spf4jConfig
  .getMeasurementRecorder(App.class + " isPrimeNumber");
Random random = new Random();
for (int i = 0; i < 100; i++) {
    long numberToCheck = random.nextInt(999_999_999 - 100_000_000 + 1) + 100_000_000;
    long startTime = System.currentTimeMillis();
    boolean isPrime = isPrimeNumber(numberToCheck);
    measurementRecorder.record(System.currentTimeMillis() - startTime);
    LOGGER.info("{}. {} is prime? {}", i + 1, numberToCheck, isPrime);
}

5.2. Запуск кода

Теперь мы готовы протестировать производительность нашей простой функции isPrimeNumber ().

Давайте запустим код и посмотрим результаты:

Time Series DB (TSDB) : E:\Projects\spf4j-core-app\spf4j-performance-monitoring.tsdb2
Time Series text file : E:\Projects\spf4j-core-app\spf4j-performance-monitoring.txt
1. 406704834 is prime? false
...
9. 507639059 is prime? true
...
20. 557385397 is prime? true
...
26. 152042771 is prime? true
...
100. 841159884 is prime? false

5.3. Просмотр результатов

Давайте запустим пользовательский интерфейс SPF4J, выполнив команду из папки проекта:

java -jar target/dependency-jars/spf4j-ui-8.6.9.jar

Это вызовет приложение пользовательского интерфейса рабочего стола. Затем в меню выберем Файл > Открыть . После этого в окне обзор найдите файл spf4j-performance-monitoring.sdb2 и откройте его.

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

Это приведет к созданию серии графиков.

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

Второй график показывает агрегированные данные, такие как min, max и average:

И последний график показывает количество измерений в зависимости от времени:

6. Использование MeasurementRecorderSource

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

6.1. Запись показателей

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

Spf4jConfig.initialize();
Random random = new Random();
for (int i = 0; i < 50; i++) {
    long numberToCheck = random.nextInt(999_999_999 - 100_000_000 + 1) + 100_000_000;
    isPrimeNumber(numberToCheck);
}

Вместо всего этого шаблонного, далее, давайте аннотируем метод isPrimeNumber () , используя @PerformanceMonitor :

@PerformanceMonitor(
  warnThresholdMillis = 1,
  errorThresholdMillis = 100, 
  recorderSource = Spf4jConfig.RecorderSourceForIsPrimeNumber.class)
private static boolean isPrimeNumber(long number) {
    //...
}

Давайте рассмотрим различные настройки:

  • warnThresholdMillis – максимальное время, разрешенное для запуска метода без предупреждающего сообщения
  • errorThresholdMillis – максимальное время, разрешенное для запуска метода без сообщения об ошибке
  • recorderSource – экземпляр MeasurementRecorderSource

6.2. Запуск кода

Давайте сначала сделаем сборку Maven, а затем выполним код, передав агент Java:

java -javaagent:target/dependency-jars/aspectjweaver-1.8.13.jar -jar target/spf4j-aspects-app.jar

Мы видим результаты:

Time Series DB (TSDB) : E:\Projects\spf4j-aspects-app\spf4j-performance-monitoring.tsdb2
Time Series text file : E:\Projects\spf4j-aspects-app\spf4j-performance-monitoring.txt

[DEBUG] Execution time 0 ms for execution(App.isPrimeNumber(..)), arguments [555031768]
...
[ERROR] Execution time  2826 ms for execution(App.isPrimeNumber(..)) exceeds error threshold of 100 ms, arguments [464032213]
...

Мы видим, что фреймворк SPF4J регистрирует время, затраченное на каждый вызов метода. И всякий раз, когда он превышает пороговое значение error Millis в 100 мс, он регистрирует его как ошибку. Аргумент, переданный методу, также регистрируется.

6.3. Просмотр результатов

Мы можем просмотреть результаты так же, как и ранее, используя пользовательский интерфейс SPF4J, поэтому мы можем обратиться к предыдущему разделу.

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

В этой статье мы рассказали об основных концепциях сбора и визуализации метрик.

Затем мы поняли возможности мониторинга производительности платформы SPF4J с помощью простого примера. Мы также использовали встроенный инструмент пользовательского интерфейса для визуализации данных.

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