Написание инструмента нагрузочного тестирования на Go
Мотивация:
В одной из моих предыдущих организаций у нас было требование нагрузочного тестирования микросервисов на основе Python и Scala, которые взаимодействовали через RabbitMQ (используя протокол AMQP 0.9, который сильно отличается от AMQP 1.0).
Мы уже использовали Jmeter в качестве предпочтительного инструмента нагрузочного тестирования для Rest API. Так что у меня было мало вариантов для работы
- Jmeter на основе AMQP плагин
- Сам RabbitMQ предоставляет грубый способ загрузки очередей через свой Тест производительности инструмент
- Последним вариантом было построить что-то свое
Инструмент Jmeter и RabbitMQ мог бы в некоторой степени служить цели, но они больше подходили для асинхронных сообщений. Некоторые из наших микросервисов использовали функциональность rpc (удаленный вызов процедур) RabbitMQ, которая больше похожа на синхронную связь между потребителем и издателем через очереди.
Теперь вопрос о том, было ли использование RPC поверх очередей сообщений правильным конструктивным решением или нет, мы обсудим в другой раз.
Вывод состоял в том, чтобы не использовать плагин Jmeter или инструмент RabbitMQ Perf Test, а создать что-то свое.
В то время я изучал Go и был очень впечатлен моделью параллелизма Go. Я подумал, что это хорошее время и возможность превратить свое обучение Go в реализацию чего-то полезного, поэтому я решил создать инструмент генератора нагрузки в Go.
Кстати, есть хороший инструмент генератора нагрузки, написанный на Go, который называется Vegeta, но он в основном для HTTP.
Прежде чем приступить к созданию инструмента, я хотел уточнить несколько вещей.
- В Go была доступна клиентская библиотека RabbitMQ, потому что RabbitMQ не предоставляет ее, к счастью, был один клиент с открытым исходным кодом. библиотека
- Мониторинг RabbitMQ с точки зрения ЦП, памяти, дискового ввода-вывода хоста, на котором работает узел RabbitMQ, а затем показателей отдельных очередей, таких как общее количество подключений, общее количество потребителей, общее количество сообщений и т. д. в данный момент времени. Есть несколько способов сделать это, как задокументировано здесь
Дизайн:
Как только все вышеперечисленное было подтверждено, я начал думать о минимальной функциональности, которую должен иметь этот инструмент, и решил следовать за дизайном Jmeter для того же самого. мне нужно было контролировать
- Количество потоков/пользователей
- Время нарастания для этих пользователей
- Время выполнения теста
- Способ определения проб/тестов
Вот и все. Если бы я мог справиться с этими 4 вещами, мой инструмент был бы готов к использованию.
Код:
Первым делом нужно было принять пользовательский ввод для No of User, Ramp Up time и Execution time. Это было довольно просто, я мог установить это с помощью флагов командной строки.
Go поощряет более короткие имена переменных. Если кому-то больше интересно узнать больше, вот один презентация
Во-вторых, нужно было рассчитать, сколько времени Go должен ждать, прежде чем он запустит следующий пользовательский поток, что также было просто, поскольку у меня было общее количество запросов no. пользователей для загрузки и время, в течение которого все пользовательские потоки должны быть запущены и работать, поэтому время ожидания между инициированием двух последовательных пользовательских потоков было
Так, например, если бы мое время нарастания составляло 30 секунд, и у меня было всего 10 пользователей, я бы запускал новый пользовательский поток каждые 3 секунды.
Теперь я хотел что-то, что могло бы увеличить количество пользовательских потоков в соответствии с расчетами нарастания. Лучшим способом было инициировать горутину для этой работы.
Горутина это облегченный поток, созданный средой выполнения Go, когда любая именованная или анонимная функция вызывается с префиксом ключевого слова
go
Ниже горутина подталкивает счетчик пользователей в канал каждый раз, когда он увеличивается, но не инициирует фактический пользовательский поток.
Канал объект в Go — это средство связи для горутин, которые взаимодействуют друг с другом путем отправки и получения сообщений и определяется ключевым словом
chan
Следующей задачей было получить временной канал, который помог бы указать окончание продолжительности выполнения.
После этого последним битом было использование двух каналов, определенных в приведенном выше коде, для управления потоком и выполнения необходимых тестов.
Этот фрагмент кода объединяет все основные функции Go, что делает параллелизм таким простым.
select
это специальный оператор в Go, который блокирует выполнение до тех пор, пока какой-либо из каналов в операторах case не будет готов к операции отправки/получения.
И это все, что мне было нужно!! Я только что добавил все необходимые тестовые вызовы в приведенном выше for
loop и смог бомбардировать очереди сообщений RabbitMQ сотнями пользователей, что потребовало чрезвычайно малого объема памяти на моем генераторе нагрузки!
Для мониторинга и отчетности я переместил все показатели в InfluxDB и использовал Grafana для визуализации в реальном времени.
Go — чрезвычайно мощный язык, когда дело доходит до параллелизма. Я бы порекомендовал каждому энтузиасту Go прочитать книгу Параллелизм в Go написано Кэтрин Кокс-Будей
Вы можете найти полную суть приведенного выше кода здесь