- PVSM.RU - https://www.pvsm.ru -

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

Нагрузочное тестирование выполнять сложно, а инструменты далеки от совершенства. Почему? - 1

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

Но потом мы пробуем действительно сгенерировать нагрузку. Это делается легко, только если ваше приложение ужасно простое, ведь тогда можно использовать что-нибудь типа Apache JMeter [1] для генерации повторяющихся запросов. Если у вас это получится, то я вам завидую: все системы, с которыми мне приходилось работать, сложнее и требовали более изощрённой схемы тестирования.

Если ваше приложение становится чуть сложнее, то вы переходите к инструментам наподобие Gatling [2]. Они позволяют симулировать виртуальных пользователей, выполняющих различные сценарии, что намного полезнее, чем простая осада [3] одного или нескольких URL. Но даже этого недостаточно, если вы пишете приложение, использующее одновременно WebSockets и HTTP-вызовы в течение долговременной сессии, а также требующее повторения по таймеру определённых действий. Возможно, я серьёзно недоглядел чего-то в документации, но мне не удалось найти способа, допустим, настроить периодическое событие, запускаемое каждые 30 секунд и выполняющее определённые действия при ответе на сообщение WebSocket, а также производящее действия по HTTP, и всё это в рамках одной HTTP-сессии. Я не нашёл такой возможности ни в одном инструменте нагрузочного тестирования (и именно поэтому написал на работе свой собственный инструмент, который надеюсь выложить в open source, если найду время на подчистку кода и отделения его от проприетарных частей).

Но предположим, что у вас есть стандартный инструмент наподобие Gatling или Locust, который работает и удовлетворяет вашим нуждам. Отлично! Теперь давайте напишем тест. По моему опыту, сейчас это самая сложная задача, потому что нам сначала нужно разобраться, как выглядит реалистичная нагрузка — вас ждёт один-три дня кропотливого изучения логов и ведения заметок по показателям сетевых инструментов в браузере при работе веб-приложения. А после того, как вы узнаете реалистичную нагрузку, то вам придётся написать код, который сводится к следующему: подмножество вашего приложения будет притворяться пользователем, общаться с API и выполнять действия, которые совершает пользователь.

И на этом ещё ничего не закончилось! Здорово, мы написали нагрузочный тест, и он реалистичен. Но задача постоянно меняется, ведь выпускаются обновления. То есть теперь у нас появилась проблема поддержки: как обеспечивать актуальность нагрузочного теста при изменении приложения? Для этого нет качественных инструментов и почти ничто вам не поможет. Придётся сделать это частью процесса и надеяться, что вы ничего не упустите. Такой ответ не радует, и именно поэтому этот аспект является одним из самых сложных при тестировании приложения.

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

Откуда берётся сложность

По сути, ситуация такова:

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

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

Симуляция пользователей. Действительно ли она нужна?

Здесь я могу ответить «да», хотя это может зависеть от конкретного приложения. В данном случае подразумевается пользователь сервиса: если у вас монолит, то это все ваши пользователи, но в случае микросервисов «пользователем» может быть один из сервисов! При создании приложений, над которыми я работал, мне удалось достичь незначительного успеха с таргетированными тестами отдельных конечных точек. Но в конечном итоге для этого требовалась такая сложная система, что это оказалось ненамного проще самого нагрузочного теста! И хотя мы получили некоторые результаты и усовершенствования, покрыть всё не удалось (в приложении могут быть взаимодействующие конечные точки) и мы не смогли получить реалистичную нагрузку.

Лучше задаться вопросом: «А когда не нужно симулировать пользователей?». Насколько я понимаю, такое бывает, когда вы знаете, что все конечные точки независимы по производительности, отсутствуют запросы с отслеживанием состояния и порядок запросов не влияет на производительность. Это довольно серьёзные допущения, и без проверки их независимости сложно быть уверенным в них, поэтому нам приходится возвращаться к написанию теста.

Вероятно, лучшее, что вы можете здесь сделать, происходит на этапе проектирования API и системы, а не на этапе тестирования. Если упростить API, то тестировать придётся гораздо меньшую площадь поверхности. Если спроектировать систему с более независимыми частями (например, когда у каждого сервиса есть своя база данных), то их проще будет тестировать по отдельности, чем монолит. К тому же, при этом вы сможете использовать более простой инструмент — двойная выгода!

Написание тестов — сложная задача. Как и их поддержка

Создавать тесты сложно, потому что требуется выполнить несколько задач: нужно разобраться, каков поток использования API и написать симуляцию этого использования. Для понимания потока необходимо понимание других систем, кроме тестируемой, и поскольку ваша система вряд ли подробно рассматривается в их документации, не будет чёткой диаграммы того, когда и что вызывается; часто приходится разбираться в логах, пока не поймёшь, какова же истинная схема использования. Далее идёт написание этой симуляции — тоже нетривиальная задача, поскольку необходимо обрабатывать состояние большого количества акторов, представляющих собой пользователей вашего API!

А, да, и теперь вам нужно писать для всего этого ещё и интеграционные тесты.

Существуют исследования о том, как упростить некоторые из этих задач. Например, можно разобраться в том, что вам нужно для первоначального теста, и распознавать регрессии (отсутствующие новые нагрузки) по автоматизированному анализу логов. Но насколько я понимаю, на GitHub, не говоря уже о коммерческой доступности, нет продукта, способного выполнить за меня эту задачу. Поэтому похоже, что в нашей отрасли подобные системы не получили широкой поддержки. Для самостоятельной реализации этот проект был бы слишком большим, и в этом может быть причина его увядания (или он реализуется в больших компаниях и о нём не говорят).

Может быть, не подвергать всё нагрузочному тестированию?

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

Чтобы получить подробную картину производительности системы, можно воспользоваться одним из способов:

  • Старый добрый анализ. Усесться с ноутбуком, ручкой и пониманием системы в целом, выделить на это полдня, и вы получите примерные расчёты основных параметров и ограничений масштабирования системы. Когда вы наткнётесь на «бутылочное горлышко» или встретитесь с неизвестными переменными (сколько транзакций в секунду может поддерживать наша база данных? Сколько мы их генерируем?), то можно будет протестировать конкретно их!
  • Разворачивание фич. Если вы можете медленно разворачивать фичи на всех пользователей, то вам может и не понадобиться нагрузочное тестирование! Вы можете измерять производительность экспериментально и проверять, достаточно ли её. Достаточно? Разворачиваем дальше. Мало? Откатываемся назад.
  • Повторение трафика. Это совершенно не поможет с новыми фичами (для них воспользуйтесь предыдущим пунктом), но поспособствует пониманию критичных точек системы для уже имеющихся фич, при этом не требуя большого объёма разработки. Вы можете взять отслеженный ранее трафик и повторять его (многократно снова и снова, даже комбинируя трафик различных временных периодов), наблюдая за производительностью системы.

    На правах рекламы

    Серверы для разработки [4] — это эпичные от Вдсины.
    Используем исключительно быстрые NVMe накопители от Intel и не экономим на железе [5] — только брендовое оборудование и самые современные решения на рынке!

    Автор: Mikhail

    Источник [6]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/vy-sokaya-proizvoditel-nost/360465

Ссылки в тексте:

[1] Apache JMeter: https://en.wikipedia.org/wiki/Apache_JMeter

[2] Gatling: https://en.wikipedia.org/wiki/Gatling_(software)

[3] осада: https://en.wikipedia.org/wiki/Siege_(software)

[4] Серверы для разработки: https://vdsina.ru/cloud-servers?partner=habr226

[5] не экономим на железе: https://habr.com/ru/company/vdsina/blog/514570/

[6] Источник: https://habr.com/ru/post/536304/?utm_source=habrahabr&utm_medium=rss&utm_campaign=536304