Инструментарий для нагрузочного тестирования и не только

cc01k9xoudv8rcebgrxt2b1gtd4.png

Мы в Altenar разрабатываем программное обеспечение для беттинга. Как и для многих разработчиков для нас очень важна производительность, в связи с этим особое внимание мы уделяем нагрузочному тестированию. Под катом подробный рассказ про наш опыт от инженера по автоматизации тестирования Даниила Матафонова (QA Automation Engineer) daniilmatafonov.

Долгое время мы использовали JMeter. Но ни команда DevOps, ни .NET разработчики не проявляли горячего интереса к его интерфейсу. Прочитать тестовый скрипт было невозможно.

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

  • Гибкость
  • Масштабируемость
  • Простота создания тестового сценария на языке понятном представителям из разных департаментов
  • Поддержка gRPC

Анализ инструментов


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

5acd9qye0e-9lzssys0k2nkq_ee.png

k6 стал для нас оптимальным инструментом по следующим причинам:

  • Поддержка протоколов http1.1/1.2 и gRPC
  • Кроссплатформенность
  • JavaScript — язык для реализации тестов
  • Возможность запуска тестов из облачной инфраструктуры k6 cloud.
  • Выгрузка статистических метрик в различные хранилища данных (Amazon CloudWatch, Apache Kafka, Cloud, CSV, Datadog, influxDB, JSON, New Relic, StatsD)
  • Активное community

Особенности k6


Сценарии

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

Исполнители

Исполнители нужны для планирования и управления ресурсами Vus (потоки выполнения, именуемые виртуальными пользователями) и iterations ─ итераций прогонов нагрузочных тестов. Исполнитель выбирается в зависимости от типа трафика, который вы хотите смоделировать для тестирования сервисов.

В k6 представлены следующие типы исполнителей:

  • Shared iterations
  • Per VU iterations
  • Constant VUs
  • Ramping VUs
  • Constant Arrival Rate
  • Ramping Arrival Rate
  • Externally Controlled

Изначально k6 работает в агрессивном режиме и пытается отправить наибольшее количество запросов в единицу времени. Мы изменили это поведение, использовав исполнитель Constant VUs. Он позволяет генерировать нагрузку с фиксированным количеством Vus на старте и параметр minIteration Duration, задает лимит для итераций и ограничение по количеству запросов для каждого VUs. Более подробную информацию можно найти в документации, раздел Executors.

Метрики

В k6 используются built-in:

  • http_reqs
  • vus
  • iterations
  • iteration_duration
  • data_received
  • data_sent
  • checks

и custom метрики следующих типов:
  • Counter — кумулятивная метрика, суммирует добавленные значения.
  • Gauge — метрика, которая хранит минимальное, максимальное и последнее добавленные к ней значения.
  • Rate — метрика, которая отслеживает процент добавленных значений, отличных от нуля.
  • Trend — метрика, позволяющая вычислять статистику по добавленным значениям (min, max, average и процентили).

Кроме встроенных мы используем кастомные метрики типов Trend для хранения времени обработки запросов в миллисекундах и Rate — для хранения частоты возникновения ошибок API. Документация по metric types.

Тестирование


Выполним нагрузочное тестирование на примере метода одного из компонентов системы с использованием k6. Рассмотрим как настроить интеграцию с InfluxDB — одним из наиболее популярных хранилищ метрик, собрать нагрузочные метрики и отобразить их в Grafana.

Конфигурационный скрипт:

config.js
export let options = {
    httpDebug: 'full',
    minIterationDuration: '1m',
    scenarios: {
        main_scenario: {
            executor: 'constant-vus',
            vus: 4000,
            exec: 'frontendMain',
            duration: '5m',
            gracefulStop: '0s',
        }
    }
};

Содержит объявление объекта options с настроенным сценарием и исполнителем.

Скрипт компонента:

При реализации вызова любого метода API достаточно использовать набор обязательных входных параметров и функцию get () или post () в зависимости от типа http-запроса.

export function getAllSports(langIds) {
    const langId = getRndValueFromArray(langIds);
    const currentDate = new Date().toISOString();
    const weekAfterDate = addDays(currentDate, 7);
    let urlQuery = buildQuery({
        timezoneOffset: "-180",
        langId: langId,
        skinName: "betsonic",
        configId: `${__ENV.CONFIG}`,
        culture: "en-GB",
        countryCode: "RU",
        deviceType: "Desktop",
        numformat: "en",
        period: 'periodall',
        startDate: currentDate,
        endDate: weekAfterDate,
    });
    let resp = http.get(capiUrl + `/Sportsbook/GetAllSports?${urlQuery}`);
    return resp;
};

default функция:
export function frontendMain() {

    group("GetAllSports", function () {
        const allSportsResult = getAllSports(langIds);
        let allSportsCheckResult = check(allSportsResult, {
            "status is 200 OK": (allSportsResult) => allSportsResult.status === 200,
            "content-type is application/json": (allSportsResult) => allSportsResult.headers['Content-Type'] === "application/json; charset=utf-8",
        });
        errorRate.add(!allSportsCheckResult);
        let allSportsDuration = getTrend("GetAllSports");
        if (Object.keys(allSportsDuration).length !== 0) {
            allSportsDuration.add(allSportsResult.timings.duration);
        }
    });

В методе реализован непосредственно сценарий теста. Функция group используется для группировки кода по тестируемым методам. С помощью функции check реализуются проверки на статус-код ответа сервера и content-type.

Интеграция с InfluxDB


Для выгрузки данных в InfluxDB используется параметр -o influxdb. В качестве значения передается адрес хоста, порт, имя базы.

Сборка и запуск тестов

Собираем тесты с помощью системы сборки webpack.

m544h2rp47yy1ta3ph-jcledsv0.png

Запускаем тест с помощью bash скрипта:

pkt7dcskclqqscf0hm8rtwdd_es.png

Через параметр -e передаются переменные среды. В тестах мы используем их для настройки тестового конфига, категорий событий, адресов тестируемых компонентов API.

Логи и метрики


Далее представлен фрагмент лога в процессе выполнения тестов.

rvaulydhl8ntbuvihiyubrelb6m.png

По завершению работы в папке logs будет сформирован отчет по тестам, содержащий метрики и дополнительную статистику.

█ GetAllSports

✓ status is 200 OK
✓ content-type is application/json

GetAllSports…: avg=1383.930035 min=6.675649 med=1258.454661 max=4541.098292 p (90)=2674.549785 p (95)=2930.894705

checks…: 100.00% ✓ 80000 ✗ 0
data_received…: 77 MB 253 kB/s
data_sent…: 12 MB 41 kB/s
errors…: 0.00% ✓ 0 ✗ 40000
group_duration…: avg=1.01s min=6.77ms med=750.99ms max=5.24s p (90)=2.2s p (95)=2.8s
http_req_blocked…: avg=48.88ms min=188ns med=510ns max=749.13ms p (90)=34.35ms p (95)=439.23ms
http_req_connecting…: avg=6.7ms min=0s med=0s max=112.68ms p (90)=3.13ms p (95)=67.97ms
http_req_duration…: avg=933.7ms min=6.27ms med=728.89ms max=4.54s p (90)=2.07s p (95)=2.67s
http_req_receiving…: avg=3.72ms min=33.03µs med=188.14µs max=384.06ms p (90)=4.67ms p (95)=15.53ms
http_req_sending…: avg=92.47µs min=19.12µs med=62.73µs max=94.61ms p (90)=122.85µs p (95)=157.34µs
http_req_tls_handshaking…: avg=35.66ms min=0s med=0s max=426.84ms p (90)=26.19ms p (95)=353.95ms
http_req_waiting…: avg=929.88ms min=8.54µs med=727.75ms max=4.54s p (90)=2.06s p (95)=2.67s
http_reqs…: 20000 131.488876/s
iteration_duration…: avg=2.03s min=17.19ms med=2.03s max=5.25s p (90)=3.51s p (95)=3.92s
iterations…: 20000 65.744438/s
vus…: 10 min=10 max=4000
vus_max…: 4000 min=4000 max=4000

Проанализируем наиболее значимые показатели по компоненту:

GetAllSports…: avg=1383.930035 — среднее время выполнения метода в ms (миллисекундах).
errors…: 0.00% — ошибок API не выявлено.
http_reqs…: 20000 131.488876/s — общее число выполненных запросов
vus…: 10 min=10 max=4000 — количество vus.

Отчеты Grafana


Для более наглядного представления метрик мы создали Grafana board. На графиках представлена статистика по выполненному нагрузочному тесту.

Показатели количества отправленных запросов (http_reqs), потоков выполнения (Virtual Users), проверок content-type ответа и статус-кода (Checks per second), количества ошибок (Errors).

hwvksjjp5voc86yuncej_85upza.png

Выводы


Нам удалось построить процесс проведения нагрузочного тестирования, вовлечь в него команду разработки и DevOps для совместного анализа и запуска тестов, а также собрать нагрузочные метрики, отобразить результаты в виде графиков в Grafana.

© Habrahabr.ru