Однажды встретились JMeter и незнакомка…

в 0:26, , рубрики: groovy, http, java, jmeter, jni, jupyter, pandas, wget, балансировка нагрузки, высокая производительность, плагин, Тестирование IT-систем, Тестирование веб-сервисов

Однажды встретились JMeter и незнакомка… - 1
Кадр из фильма «Дом у озера». Встреча (www.kinopoisk.ru)

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

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

Предысловие

Однажды встретились JMeter и незнакомка… - 2
Популярные варианты отправки запросов к неизвестной системе

Использовал такие варианты отправки запросов к незнакомой системе:

  1. Создал промежуточное веб-приложение, умеющее отправлять запросы тестируемой системе, а команды веб-приложению отправлял с помощью компонента HTTP Request.
  2. Создал промежуточный веб-сервис, умеющий отправлять запросы тестируемой системе, а команды веб-сервису отправлял с помощью компонента SOAP/XML-RPC Request.
  3. Создал консольный клиент к тестируемой системе, вызывал клиент из JMeter, используя компонент OS Process Sampler.
  4. Создал библиотеку (API) на Java, C# для работы с тестируемой системой и вызывал методы библиотеки, используя компонент JSR223 Sampler.
  5. Создал плагин для JMeter.

Веб-приложение и HTTP Request или веб-сервис и SOAP/XML-RPC Request

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

Пример. Тестировал wcf-сервис, работающий по протоколу SOAP/MSBin1 поверх HTTP. JMeter не умеет работать с протоколом SOAP/MSBin1, отправить небольшой запрос одним пакетом сможет в очень нечитаемом виде, а вот отправить большой запрос или получить большой ответ потоком уже не сможет.
Первое решение было таким, используя Visual Studio сгенерировал клиента к тестируемой системе (wcf-сервису), и создал тестовый веб-сервис, который мог по протоколу HTTP принимать команды от JMeter.

Или к тестируемой системе можеть быть API на Python. И для вызова Python-кода можно создать веб-приложение на Python, которое будет принимать команды от JMeter и подавать нагрузку на систему.

Веб-приложение для небольшой нагрузки

Однажды встретились JMeter и незнакомка… - 3
Веб-приложения для создания небольшой нагрузки на тестируемую систему. Важно вести лог в веб-приложении.

При работе с веб-приложением всегда будет временной лаг. Время взаимодействия JMeter с промежуточным веб-приложением будет ненулевым даже при малой нагрузке. Поэтому время ответа на запросы тестируемой системы нужно измерять по логам промежуточного веб-приложения. Время обработки запросов, фиксируемое в JMeter, будет заведомо большим. Поэтому важно вести два лога — лог в JMeter и лог в промежуточном веб-приложении. Уметь парсить лог веб-приложения, строить по нему статистику.

Веб-приложение под большой нагрузкой

Однажды встретились JMeter и незнакомка… - 4
Веб-приложение под большой нагрузкой может стать точкой отказа, если оно не мастшабируется.

При создании большой нагрузки нередко приходится запускать несколько JMeter. Создаётся много запросов, с которым промежуточное веб-приложение может не справится. Временной лаг будет расти. При большой загрузке даже логирование в веб-приложении может запаздывать, появится дополнительный временной лаг при фиксировании времени ответа тестируемой системы. Изобразил это на рисунке выше выделив промежуточное веб-приложение красным, а стрелку к файлу лога пунктирной.

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

Перед подачей большой нагрузки важно:

  • выставить требования и определить максимальную производительность промежуточного веб-приложения;
  • учесть при тестировании способ балансировки на тестируемой системе.

Однажды встретились JMeter и незнакомка… - 5
Для создания большой нагрузки нужно использовать несколько JMeter и несколько промежуточных веб-приложений, и нагрузка распределится равномерно

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

Тогда нагрузка будет распределяться равномерно, даже если балансировщик тестируемой системы использует привязку клиентов к серверам приложений по ip-адресам.

Для обработки множества логов и формирования статистики по ним удобно использовать библиотеку Pandas (из Python). И писать лог веб-приложений так, чтобы получался сразу CSV-файл. Такие CSV-файлы можно в цикле загружать, анализировать и сразу строить графики, используя MatPlotLib. Jupyther, Pandas и MatPlotLib — верные помощники при обработке логов.

Консольное приложение и OS Process Sampler

Вариант подачи нагрузки через запуск приложений оправдан, если:

  • есть готовый удобный клиент в виде приложения для работы с тестируемой системой;
  • есть готовые модульные тесты к тестируемой системе и нужно лишь использовать JMeter для запуска необходимого количества потоков, выполняющих эти тесты и замера времени их выполнения;
  • нужно много использовать криптографию, вы знаете C#/Java и умеете работать с криптографией на C#/Java;
  • к тестируемой системе есть SDK на .NET/Win32/Java/Python и вы знаете C#/C++/Java/Python и можете легко написать консольный клиент по примерам из SDK;
  • нужно скачивать или отправлять огромные файлы и стандартные компоненты JMeter потребляют слишком много ресурсов при выполнении таких запросов;
  • при работе с тестируемой системой сложный сценарий, который проще реализовать и отладить в IDE (Intellij IDEA, Eclipse, KDevelop, Visual Studio, ...), чем формировать в JMeter.

Однажды встретились JMeter и незнакомка… - 6
Подача нагрузки на тестируемую систему через запуск клиентских приложений

Первый раз использовал нагрузку через консольное приложение, когда в тесте надо было скачивать файлы размером более 100 МБайт и до нескольких гигабайт по протоколу HTTP. JMeter при получении таких ответов потреблял всю доступную оперативную память. На помощь пришел wget, этот консольный инструмент без труда скачивает огромные файлы, потребляя минимум ОЗУ, имеет настройки позволяющие включить и отключить gzip и keepalive. OS Process Sampler и wget позволили прокачать канал и выполнить тестирование, чего нельзя было добиться используя HTTP Request.

Особенности использования приложений:

  • приложение это отдельный процесс, и он потребляет много оперативной памяти, для создания большой нагрузки понадобится много ОЗУ, быстрый процессор или несколько нагрузочных станций;
  • каждое приложение будет вести свой отдельный лог, нужно позаботится, чтобы множество логов потом было удобно обработать и получить по ним статистику;
  • слишком расточительно запускать приложение для выполнения лишь одного короткого запроса к тестируемой системе, удобно реализовывать в приложении целый сценарий: аутентификация, несколько итераций полезных действий и выход.

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

Чтобы консольное приложение умело выполнять сценарий, удобно реализовать в нём приём сценария из стандартного входа, используя свой псевдокод. Например, такой:

#AuthByCertificate
"Иванов Иван Иванович"
#GetInboxDocuments
#GetContagentList
#SendDocument
".dataDocument1.txt"
#SendDocument
".dataDocument2.jpg"
#Exit

И написать парсер для такого сценария: если строка начинается на #, значит это команда, передаётся управление на обработчик такой команды. При необходимости обработчик считывает параметры, необходимые для выполнения, например, название сертификата пользователя или путь к файлу.

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

ConsoleApp.exe /test1 "Иванов Иван Иванович" ".dataDocument1.txt" ".dataDocument2.jpg"
ConsoleApp.exe /test2 "UserLogon1" "UserPassword1" ".dataDocument1.txt"

Количество сценариев, обычно ограничено и обговаривается до тестирования. Поэтому такой вариант также удобен.

Используя OS Process Sampler можно передавать любое количество параметров. Можно использовать переменные JMeter, считывать параметры из CSV-файла. Но практика показала, что удобнее написать командный файл для запуска консольного приложения. И вызывать из OS Process Sampler командный файл, а не само приложение.

Если сценарий должен возвращать какой-то результат в тест. То удобно реализовать вывод в консоль этого результата в виде строки. А на стороне JMeter реализовать пост-процессор, чтобы обрабатывать параметры ответа.

Если используется готовое приложение и оно не возвращает определённые коды ответа при успехах и ошибках. То минимально-необходимый post-процессор нужен, чтобы выставить статус работы OS Process Sampler — успешно выполнилось приложение или нет.

Пример для запуска wget и обработки результата его работы пост-процессором

Командный файл wget-download.gzip.bat для скачивания большого файла с выставленным ограничением скорости с помощью wget. Командный файл принимает на вход один параметр — адрес файла, который нужно скачать, значение параметра передаётся из OS Process Sampler. Текст wget-download.gzip.bat:

@title %~nx0
@setlocal
@cd /d %~dp0
@rem 100 MBit/sec
@rem --progress=dot:default (  1 KByte per line)
@rem --progress=dot:binary  (384 KByte per line)
@rem --progress=dot:mega    (  3 MByte per line)

%~dp0wget/bin/wget.exe -S --limit-rate=20m --progress=dot:mega ^
	--header "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:44.0) Gecko/20100101 Firefox/44.0" ^
	--header "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" ^
	--header "Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3" ^
	--header "Accept-Encoding: gzip, deflate" ^
	--output-document=- %1 1>NUL

JSR223 PostProcessor будет использовать скрипт на языке Groovy, выполняющий разбор ответа, выводимого на консоль приложением wget, и заполняющий соответствующие поля для OS Process Sampler, чтобы в логе JMeter были все необходимые данные для анализа ответов. Важно использовать именно Groovy, так как он компилируется и выполняется быстрее других, см. статью JMeter: забудьте про BeanShell Sampler от greatvovan. Текст обработчика:

// prev - http://jmeter.apache.org/api/org/apache/jmeter/samplers/SampleResult.html
import java.util.regex.Matcher;  
import java.util.regex.Pattern;

//Length: 1718181 (1.6M) [text/html]
Pattern pLength = Pattern.compile("(?im)^Length[:] ([0-9]+) ");
Matcher mLength = pLength.matcher(prev.getResponseDataAsString());
if(mLength.find())
{
	int Length = Integer.parseInt(mLength.group(1));
	prev.setBodySize(Length);
}

Pattern pContentType = Pattern.compile("(?im)^[ ]{2}Content[-]Type[:] (.*)");
Matcher mContentType = pContentType.matcher(prev.getResponseDataAsString());
if(mContentType.find())
{
	String ContentType = mContentType.group(1);
	prev.setContentType(ContentType);
}

Pattern pHeaders = Pattern.compile("(?im)^[ ]{2}([a-zA-Z].*[:].*)");
Matcher mHeaders = pHeaders.matcher(prev.getResponseDataAsString());
String headers = "";
while(mHeaders.find())
{
	headers = headers + mHeaders.group(1) + "n";
}
prev.setResponseHeaders(headers);
	
//  HTTP/1.1 200 OK
Pattern pCodeMessage = Pattern.compile("(?im)^[ ]{2}HTTP[/]1[.][0-9] ([0-9]+) (.*)");
Matcher mCodeMessage = pCodeMessage.matcher(prev.getResponseDataAsString());
if(mCodeMessage.find())
{
	String Code = mCodeMessage.group(1);
	String Message = mCodeMessage.group(2);

	if(Code=="200")
	{
		prev.setSuccessful(true);
	}
	else
	{
		prev.setSuccessful(false);
	}

	prev.setResponseCode(Code);
	prev.setResponseMessage(Message);
}

Библиотека и JSR223 Sampler

Однажды встретились JMeter и незнакомка… - 7

Хоть приложения работают прекрасно. Всё же хочется тратить меньше ресурсов — создавать потоки, а не процессы, ведь иногда нет нескольких нагрузочных станций и гигабайтов ОЗУ. Хочется принимать на вход любые переменные JMeter. А также иногда, хочется большей гибкости при формировании сценариев, хочется вместо отправки псевдокода на stdin приложения написать просто:

TestClient client = new TestClient();

String certificateCommonName = vars.get("certificateCommonName"); //"Иванов Иван Иванович"
String documentPath1 = vars.get("documentPath1"); //".dataDocument1.txt"
String documentPath2= vars.get("documentPath2"); //".dataDocument2.jpg"

client.authByCertificate(certificateCommonName); 
client.getInboxDocuments();
client.getContagentList();
client.sendDocument(documentPath1);
client.sendDocument(documentPath2);
client.exit();

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

Или выполнять аутентификацию и выход лишь один раз на каждого виртуальнульного пользователя. А в рамках одного запроса из JMeter выполнять лишь один запрос к тестируемой системе без дополнительных временных лагов.

Есть такой способ — использовать JSR223 Sampler, из которого удобно вызывать методы библиотек.

У JSR223 Sampler есть ряд удобных особенностей.

Можно разделить на три JSR223 Sampler инициализацию библиотек, создание клиента, подключение к системе. И в рамках одного потока (виртуального пользователя JMeter) операции выполнятся успешно, что позволит замерить время каждого шага.

Инициализация клиента к тестируемой системе (библиотека для .NET, загружается используя jni4net). Загружать библиотеки достаточно один раз для теста:

import net.sf.jni4net.Bridge;

import java.net.URL;

Bridge.init();
Bridge.setDebug(false);
Bridge.setVerbose(false);

java.io.File clientDllFile = new java.io.File("TestClient.j4n.dll");

Bridge.LoadAndRegisterAssemblyFrom(clientFile);

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

import testClient.Client;
// Создание объекта
Client client = new Client();
// Сохранение объекта в переменную "testClient" JMeter
vars.putObject("testClient", client);
// Результат выполнения последней команды запивается в качестве тела ответа
// пусть ответом будет текстовое представление переменной client
String responseData = client;

А дальше можно вызывать любые методы этого клиента, например, authByCertificate:

import testClient.Client;
// Получить клиента, созданного ранее
Client client = vars.getObject("testClient");
// Получить параметры для выполнения метода
String certificateCommonName = vars.get("certificateCommonName"); //"Иванов Иван Иванович"
// Вызвать метод с параметром
client.authByCertificate(certificateCommonName);

Используя jni4net, можно вызывать методы библиотек на .NET. Используя просто jni, можно вызывать методы библиотек Win32. Если библиотека уже написана на Java, то вообще не понадобится дополнительных прослоек. Конечно, так нельзя вызывать код на Python, PHP, bash, но это не всегда и нужно.

Язык Groovy, используемый в JSR223 Sampler позволяет писать компилируемые, быстрые сценарии.

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

Потребление ресурсов сведено к минимуму. Нужно лишь уметь писать код на Groovy, а он очень похож на Java.

Плагин JMeter

Если не хочется писать код. И при подаче нагрузки не нужно выполнять десятки методов, а достаточно выполнять однотипную операцию, но с сотней значений параметров. То удобно разработать плагин для JMeter. Это как библиотека, но с графическим интерфейсом.

Однажды встретились JMeter и незнакомка… - 8
Плагин для JMeter

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

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

Пару дней с отладчиком дадут понимание того, как разработать плагин.

Например, плагин TailSampler (см. статью TailSampler — паралельная отправка GET-запросов в Apache.JMeter), является комбинацией компонентов:

И содержит лишь пару сотен строк оригинального кода.

Заключение

Однажды встретились JMeter и незнакомка… - 9
Кадр из фильма «Дом у озера». Способ общения (www.movpins.com)

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

Универсального способа не существует. В одной ситуации удобнее написать плагин. В другой использовать консольное приложение. Иногда библиотеку и скрипты на Groovy. А, бывает, хочется и свой веб-сервер поднять.

Всё возможно, надо только верить.

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

Автор: polarnik

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js