IBM MQ и JMeter: Первый контакт

    Привет, Хабр!

    Это приквел моей предыдущей публикации и в то же время ремейк статьи Автоматизированное тестирование сервисов, использующих протокол MQ с помощью JMeter.

    На этот раз расскажу о своем опыте примирения JMeter и IBM MQ для счастливого тестирования приложений на IBM WAS. Сталкивался с такой задачей, легко она не поддавалась. Хочу помочь сэкономить время всем заинтересованным.



    Введение


    О проекте: шина данных, множество xml-сообщений, три области обмена (очереди, БД, файловая система), веб-сервисы со своей логикой обработки сообщений. По мере развития проекта тестировать вручную становилось всё сложнее. На помощь был призван Apache JMeter — мощный и опенсорсный, с большим сообществом пользователей и дружелюбным интерфейсом. Легкость кастомизации версии «из коробки» позволяет покрыть любые кейсы, а обещание ведущего разработчика помочь если что (таки помог) окончательно утвердило в выборе.

    Приготовление начального контекста


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


    Рисунок 1: Добавление начального контекста

    Выбрать файловый тип контекста и директорию для хранения .bindings файла, который будет содержать описание JNDI-объектов:


    Рисунок 2: Выбор типа начального контекста

    После чего можно приступать к созданию этих объектов. И начать с фабрики соединений:


    Рисунок 3: Создание фабрики соединений

    Выбрать понятное имя…


    Рисунок 4: Выбор имени фабрики соединений

    … и тип Queue Connection Factory:


    Рисунок 5: Выбор типа фабрики соединений

    Протокол — MQ Client для возможности взаимодействия с MQ удаленно:


    Рисунок 6: Выбор протокола фабрики соединений

    На следующем шаге можно выбрать уже существующую фабрику и дальнейшие настройки скопировать с нее. Жмем Next, если таковой нет:


    Рисунок 7: Выбор настроек существующей фабрики соединений

    В окне выбора параметров достаточно задать три. На вкладке Connection указать имя менеджера очередей и ip стенда с его расположением (порт 1414 оставить):


    Рисунок 8: Настройка параметров фабрики соединений

    И на вкладке Channels — канал для соединения. Нажать Finish для завершения:


    Рисунок 9: Завершение создания фабрики соединений

    Теперь создадим подключение к очереди:


    Рисунок 10: Создание целевого объекта

    Выберем понятное имя (предпочитаю указывать реальное имя очереди) и тип Queue:


    Рисунок 11: Выбор имени и типа целевого объекта

    По аналогии с Рисунком 7 можно скопировать настройки с существующей очереди. Также жмем Next, если она первая:


    Рисунок 12: Выбор настроек существующего целевого объекта

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


    Рисунок 13: Завершение создания целевого объекта

    Подготовка JMeter


    Подготовка JMeter заключается в добавлении библиотек, необходимых для взаимодействия с MQ. Они располагаются в %wmq_home%/java/lib. Скопируйте их в %jmeter_home%/lib/ext перед запуском JMeter.

    • com.ibm.mq.commonservices.jar
    • com.ibm.mq.headers.jar
    • com.ibm.mq.jar
    • com.ibm.mq.jmqi.jar
    • com.ibm.mq.pcf.jar
    • com.ibm.mqjms.jar
    • dhbcore.jar
    • fscontext.jar
    • jms.jar
    • jta.jar
    • providerutil.jar


    Альтернативный список, предложенный polarnik в комментарии с небольшим нюансом: javax.jms-api-2.0.jar вместо jms.jar.
    С jms.jar возникает ошибка NoClassDEfFoundError, решение которой нашел тут.

    • com.ibm.mq.allclient.jar
    • fscontext.jar
    • javax.jms-api-2.0.jar
    • providerutil.jar


    Оба списка библиотек успешно работают с JMeter 5.0 и IBM MQ 8.0.0.4.

    Настройка тест-плана


    Необходимый и достаточный набор элементов JMeter выглядит так:


    Рисунок 14: Тест-план

    В примере тест-плана пять переменных. Несмотря на малое их количество, рекомендую заводить отдельные конфигурационные элементы под разные типы переменных. По мере разрастания тестов это существенно упростит навигацию. В данном случае получается два списка. Первый содержит параметры подключения к MQ (см. Рисунок 2 и Рисунок 4):


    Рисунок 15: Параметры подключения к MQ

    Второй — имена целевых объектов, ссылающихся на очереди:


    Рисунок 16: Параметризованные имена очередей

    Остается настроить JMS Publisher для загрузки тестового сообщения в исходящую очередь:


    Рисунок 17: Настройка JMS Publisher

    И JMS Subscriber для вычитывания сообщения из входящей очереди:


    Рисунок 18: Настройка JMS Subscriber

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

    Заключение


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

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

    Берегите своё время. И спасибо за внимание.

    image
    Поделиться публикацией

    Комментарии 12

      –1

      Зачем ты некрофилишь IBM?

        +1
        Предпочел бы без перверсий.
        Выбери на свой вкус — «классика» или «что мертво, умереть не может»
        +2
        Какая же это все х… (я об IBM)

        Но спасибо за статью, помогла
          +2
          Предполагая, что JMS Publiser и JMS Subscriber предназначены только для топиков, а не для очередей. Что предположил после просмотра скриншота в документации на Publisher:
          JMS Publiser: dinamicTopics/myStaticTopic1
          dinamicTopics/myStaticTopic1
          указано:
          dinamicTopics/myStaticTopic1


          И не читая саму документацию :). Не рассматривал связку Publisher+Subscriber для очередей.
          А просто использовал для отправки и получения сообщений из IBM MQ компонент JMS Point-to-Point, который с очередями работает — проверено.

          Есть ли отличие между
          • JMS Publisher + JMS Subscriber
          • JMS Point-to-Point (request_only) + JMS Point-to-Point (read)

          в удобстве использования?

          Статья отличная, спасибо!
            +2
            Спасибо, что обратили внимание на пару JMS Point-to-Point (request_only) + JMS Point-to-Point (read). Попробовал передачу — справляется не хуже JMS Publisher + JMS Subscriber.

            Использовал ранее JMS Point-to-Point (request_reply) и столкнулся с тем, что кроме запроса по jms-селектору сэмплер вычитывает и все другие сообщения в очереди. Вероятно, я не до конца разобрался в его настройках.
            хм..
            Попробовал сейчас аналогичный кейс — и всё прошло хорошо. Возможно, влияние оказывало то, что несколько JMS Point-to-Point (request_reply) из разных трэдов, запущенных одновременно, смотрели в одну очередь и «мешали» друг другу…

            Не стал разбираться и по той причине, что уже были надежные рабочие кейсы с JMS Publisher + JMS Subscriber.
            Дополнительные плюсы этой пары в возможности передавать рандомные файлы из заданной директории и выбор типа передаваемого сообщения (text/bytes в первую очередь).
            В документации JMS Publiser и JMS Subscriber очереди тоже упомянуты :)
              +1
              Тоже оценил, что в JMS Publisher + JMS Subscriber есть разные форматы, есть возможность передать Object, который сериализуется в XML самостоятельно, прочитать файл — удобно. Думаю изучить эту связку.
              Ещё раз спасибо
            +2
            При подключении библиотек использую комбо-набор из:
            • com.ibm.mq.allclient.jar — 8,2 МБайт
            • fscontext.jar — 22,8 кБайт
            • jms.jar — 58,3 кБайт
            • providerutil.jar — 77,1 кБайт

            Архив allclient содержит в себе многие отдельные компоненты. Проверил — это минимальный набор, необходимый для работы с IBM MQ из Apache.JMeter. Он короче набора, указанного в статье за счёт большого allclient.jar.

            Пришел к нему из ответа Jmeter JMS point to point to connect IBM MQ error ( java.lang.IllegalStateException: QueueConnectionFactory expected, but got com.ibm.mq.jms со stackoverflow.

            Библиотеки из ответа на stackoverflow ...
            I believe you don't need that many jars, you should download the relevant 8.x.x.x-WS-MQ-Install-Java-All.jar package from the Fix Central and come up with libraries like:

            com.ibm.mq.allclient.jar
            com.ibm.mq.traceControl.jar
            fscontext.jar
            jms.jar
            JSON4J.jar
            providerutil.jar


            Провалидировал. На сколько помню, com.ibm.mq.traceControl.jar есть в allclient.jar или просто не всегда нужная библиотека — активируется только в режиме трассировки, который редко используется для JMeter, ведь в этом режиме весь трафик между JMeter и IBM MQ пишется в файл, и тест становится медленным. А библиотека JSON4J.jar не нужна — не нашел её в последних сборках IBM MQ, и без неё работает тест.

            Удобно, что в статье и библиотеки приведены. Одно пожелание — чтобы быстрее находили, не прятать их. Пусть список длинный, но будет виден.
              +2
              Комбо-набор работает отлично после обновления версии jms.jar до версии 2.0.
              Спасибо за подсказку!
              Включил предложенный список в статью и вытащил исходный из спойлера.
              +1
              Спасибо за отличную статью! Однако, есть вопрос: можно ли каким-то образом вызывать некий кастомный код по модификации файла перед его отправкой?
              В моём случае, все отправляемые файлы должны различаться двумя фрагментами: первый фрагмент содержит число, которое в каждом файле инкрементируется на единичку, а второй фрагмент (концевой) содержит строчку, содержащую HMAC от предшествующего содержимого и вычисляемую путём вызова некоей внешней Java-библиотеки.
                +1
                Lexandiy, если правильно понял вопрос — можно.
                Делаю это следующим образом:
                1. В JMS Publisher выбираю опцию From File и указываю путь к файлу
                2. В самом файле параметризую необходимые участки (в моем случае, значение тегов в XML):
                <ns0:RequestId>${rquid}</ns0:RequestId>

                3. Эти параметры описываю в объекте тест-плана User Parameters:
                rquid=${__Random(1000000000000000000,9000000000000000000)}

                4. Перед отправкой JMeter вставляет сгенерированные значения параметров в тело XML-сообщения

                Хочу поговорить об этом в следующей публикации.

                В вашем случае параметризация будет более сложной — вероятно, придется использовать JSR223 PreProcessor для обращения к внешней библиотеке и передать полученный от нее ответ в параметр JMeter (+ второй параметр, который инкрементируется).

                  +1
                  Спасибо! Я, в итоге, понаписал на все случаи жизни классов, наследующих от AbstractJavaSamplerClient (IncrementHelper, HmacCalculator etc), положил JAR-файл с ними в папку %jmeter_home%/lib/ext, а дальше — Add/Sampler/Java Request + задание соотв. Classname.
                +1
                Столкнулся с проблемой во всех реализациях работы с IBM MQ из JMeter (5.0):

                Дублирующиеся client ID при работе JMS Point-to-Point и других компонентов JMeter
                Основная ошибка в checkDuplicateClientID из com.ibm.mq.allclient-9.0.0.5.jar:
                ERROR o.a.j.p.j.s.JMSSampler: JMSCC0111: IBM MQ classes for JMS attempted to set a pre-existing client ID on a Connection or JMSContext.
                com.ibm.msg.client.jms.DetailedInvalidClientIDException: JMSCC0111: IBM MQ classes for JMS attempted to set a pre-existing client ID on a Connection or JMSContext.
                  at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:1.8.0_191]
                  at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[?:1.8.0_191]
                  at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[?:1.8.0_191]
                  at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[?:1.8.0_191]
                  at com.ibm.msg.client.commonservices.j2se.NLSServices.createException(NLSServices.java:319) ~[com.ibm.mq.allclient]
                  at com.ibm.msg.client.commonservices.nls.NLSServices.createException(NLSServices.java:226) ~[com.ibm.mq.allclient]
                  at com.ibm.msg.client.jms.internal.JmsErrorUtils.createException(JmsErrorUtils.java:126) ~[com.ibm.mq.allclient]
                  at com.ibm.msg.client.jms.internal.JmsConnectionImpl.checkDuplicateClientID(JmsConnectionImpl.java:304) ~[com.ibm.mq.allclient]
                  at com.ibm.msg.client.jms.internal.JmsConnectionImpl.<init>(JmsConnectionImpl.java:252) ~[com.ibm.mq.allclient]
                  at com.ibm.msg.client.jms.internal.JmsQueueConnectionImpl.<init>(JmsQueueConnectionImpl.java:67) ~[com.ibm.mq.allclient]
                  at com.ibm.msg.client.jms.admin.JmsConnectionFactoryImpl._createConnection(JmsConnectionFactoryImpl.java:276) ~[com.ibm.mq.allclient]
                  at com.ibm.msg.client.jms.admin.JmsConnectionFactoryImpl.createConnection(JmsConnectionFactoryImpl.java:236) ~[com.ibm.mq.allclient]
                  at com.ibm.mq.jms.MQConnectionFactory.createCommonConnection(MQConnectionFactory.java:6016) ~[com.ibm.mq.allclient]
                  at com.ibm.mq.jms.MQQueueConnectionFactory.createQueueConnection(MQQueueConnectionFactory.java:111) ~[com.ibm.mq.allclient]
                  at org.apache.jmeter.protocol.jms.sampler.JMSSampler.threadStarted(JMSSampler.java:664) [ApacheJMeter_jms-5.0.jar:5.0 r1840935]
                  at org.apache.jmeter.threads.JMeterThread$ThreadListenerTraverser.addNode(JMeterThread.java:762) [ApacheJMeter_core-5.0.jar:5.0 r1840935]
                  at org.apache.jorphan.collections.HashTree.traverseInto(HashTree.java:994) [jorphan-5.0.jar:5.0 r1840935]
                  at org.apache.jorphan.collections.HashTree.traverseInto(HashTree.java:995) [jorphan-5.0.jar:5.0 r1840935]
                  at org.apache.jorphan.collections.HashTree.traverseInto(HashTree.java:995) [jorphan-5.0.jar:5.0 r1840935]
                  at org.apache.jorphan.collections.HashTree.traverse(HashTree.java:977) [jorphan-5.0.jar:5.0 r1840935]
                  at org.apache.jmeter.threads.JMeterThread.threadStarted(JMeterThread.java:730) [ApacheJMeter_core-5.0.jar:5.0 r1840935]
                  at org.apache.jmeter.threads.JMeterThread.initRun(JMeterThread.java:718) [ApacheJMeter_core-5.0.jar:5.0 r1840935]
                  at org.apache.jmeter.threads.JMeterThread.run(JMeterThread.java:249) [ApacheJMeter_core-5.0.jar:5.0 r1840935]
                  at java.lang.Thread.run(Thread.java:748) [?:1.8.0_191]
                


                Наведённая ошибка уже при работе с очередями:
                ERROR o.a.j.p.j.s.JMSSampler: Error browsing queue queue://ManagerName/QName with selector  and configured timeout 20
                java.lang.NullPointerException: null
                  at org.apache.jmeter.protocol.jms.sampler.JMSSampler.browseQueueForConsumption(JMSSampler.java:331) [ApacheJMeter_jms-5.0.jar:5.0 r1840935]
                  at org.apache.jmeter.protocol.jms.sampler.JMSSampler.handleRead(JMSSampler.java:271) [ApacheJMeter_jms-5.0.jar:5.0 r1840935]
                  at org.apache.jmeter.protocol.jms.sampler.JMSSampler.sample(JMSSampler.java:204) [ApacheJMeter_jms-5.0.jar:5.0 r1840935]
                  at org.apache.jmeter.threads.JMeterThread.doSampling(JMeterThread.java:622) [ApacheJMeter_core-5.0.jar:5.0 r1840935]
                  at org.apache.jmeter.threads.JMeterThread.executeSamplePackage(JMeterThread.java:546) [ApacheJMeter_core-5.0.jar:5.0 r1840935]
                  at org.apache.jmeter.threads.JMeterThread.processSampler(JMeterThread.java:486) [ApacheJMeter_core-5.0.jar:5.0 r1840935]
                  at org.apache.jmeter.threads.JMeterThread.run(JMeterThread.java:253) [ApacheJMeter_core-5.0.jar:5.0 r1840935]
                  at java.lang.Thread.run(Thread.java:748) [?:1.8.0_191]
                

                При любых таймаутах и настройках.



                Происходит например в строке
                github.com/apache/jmeter/blob/v5_0/src/protocol/jms/org/apache/jmeter/protocol/jms/sampler/JMSSampler.java#L331
                consumer = session.createReceiver(queue, jmsSelector);


                Причина — если для фабрики соединений указать значение для Общие/Идентификатор клиента отличное от пустого. То в результате получается такая ошибка.

                Исправление — не заполнять поле. В текущей статье, про него не написано, и это правильно.

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

                Самое читаемое