Как решить проблему с производительностью jms-сервера, когда клиент использует временную очередь replyTo?

В настоящее время я создаю серверное приложение Mule ESB, которое использует jms-коннектор типа «запрос-ответ». Поскольку он используется в высококонкурентной среде, мы включили кеш Spring JMS в нашей конфигурации MQ.

<spring:beans>
    <mule>
        <!-- MQ Factory -->
        <spring:bean id="testMsgMqFactoryBean1" name="testMsgMqFactory1" class="com.ibm.mq.jms.MQQueueConnectionFactory">
            <spring:property name="channel" value="${test.msg.mq.channel.1}" />
            <spring:property name="queueManager" value="${test.msg.mq.queueManager.1}" />
            <spring:property name="hostName" value="${test.msg.mq.hostName.1}" />
            <spring:property name="port" value="${test.msg.mq.port.1}" />
            <spring:property name="transportType" value="${mq.jms.transportType}" />
        </spring:bean>
        <spring:bean id="testMsgMqFactoryBeanCache1" class="org.springframework.jms.connection.CachingConnectionFactory">
            <spring:property name="targetConnectionFactory" ref="testMsgMqFactoryBean1" />
            <spring:property name="sessionCacheSize" value="${test.threading.profile.maxThreadsActive}" />
            <spring:property name="cacheConsumers" value="false" />
            <!-- <spring:property name="cacheProducers" value="false" /> -->
        </spring:bean>
        <!-- MQ Connector 1 -->
        <jms:custom-connector name="testMsgMqConnector.1" class="org.mule.transport.jms.websphere.WebsphereJmsConnector" doc:name="Custom JMS">
            <spring:property name="specification" value="1.1" />
            <spring:property name="connectionFactory" ref="testMsgMqFactoryBeanCache1" />
            <spring:property name="persistentDelivery" value="false" />
            <spring:property name="disableTemporaryReplyToDestinations" value="true" />
            <spring:property name="numberOfConsumers" value="${test.threading.profile.maxThreadsActive}" />
            <spring:property name="maxRedelivery" value="-1" />
            <receiver-threading-profile maxThreadsActive="${test.threading.profile.maxThreadsActive}" maxBufferSize="${test.threading.profile.maxBufferSize}" maxThreadsIdle="${test.threading.profile.maxThreadsIdle}"/>
            <reconnect frequency="${mq.jms.reconnection.frequency}" count="${mq.jms.reconnection.count}" blocking="false" />
        </jms:custom-connector>

        <!-- msgworks inbound and outbound MQ setup -->
        <!-- Rewards -->
        <jms:endpoint exchange-pattern="request-response" queue="${test.msg.mq.inbound.account.queue}" name="testQueue1" connector-ref="testMsgMqConnector.1" doc:name="JMS" />
    </mule>
</spring:beans>

Эта конфигурация работает нормально, когда клиент использует статическую очередь replyTo. Однако у нас есть некоторые клиенты, которые используют динамическую/временную очередь ответа на запросы. Поскольку org.springframework.jms.connection.CachingConnectionFactory кэширует производителей, для каждой временной очереди replyTo объект производителя кэшируется и никогда не закрывается. После обработки сотен запросов приложение начало выдавать исключения:

********************************************************************************
Message               : Failed to create and dispatch response event over Jms destination "queue://QMGR1/TESTret5a975v53AF980F2006BE02?targetClient=1". Failed to route event via endpoint: null. Message payload is of type: JMSTextMessage
Code                  : MULE_ERROR-42999
--------------------------------------------------------------------------------
Exception stack is:
1. MQJE001: Completion Code 2, Reason 2017 (com.ibm.mq.MQException)
  com.ibm.mq.MQQueueManager:2808 (null)
2. MQJMS2008: failed to open MQ queue TESTret5a975v53AF980F2006BE02(JMS Code: MQJMS2008) (javax.jms.ResourceAllocationException)
  com.ibm.mq.jms.MQQueueServices:398 (http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/jms/ResourceAllocationException.html)
3. Failed to create and dispatch response event over Jms destination "queue://QMGR1/TESTret5a975v53AF980F2006BE02?targetClient=1". Failed to route event via endpoint: null. Message payload is of type: JMSTextMessage (org.mule.api.transport.DispatchException)
  org.mule.transport.jms.JmsReplyToHandler:173 (http://www.mulesoft.org/docs/site/current3/apidocs/org/mule/api/transport/DispatchException.html)

Изучив код ошибки MQ (MQJE001: код завершения 2, причина 2017), я обнаружил, что причина этой ошибки заключается в том, что мы никогда не закрывали производителей, а производители исчерпали дескрипторы MQ в диспетчере очередей. Быстрое и простое решение — раскомментировать строку в конфигурации кеша spring jms, чтобы каждый раз закрывать производителей.

<spring:bean id="testMsgMqFactoryBeanCache1" class="org.springframework.jms.connection.CachingConnectionFactory">
    <spring:property name="targetConnectionFactory" ref="testMsgMqFactoryBean1" />
    <spring:property name="sessionCacheSize" value="${test.threading.profile.maxThreadsActive}" />
    <spring:property name="cacheConsumers" value="false" />
    <spring:property name="cacheProducers" value="false" />
</spring:bean>

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

Мой вопрос в том, как справиться с этим сценарием? Поскольку клиент не изменит способ получения ответных сообщений из временных очередей, как мы можем избежать исчерпания обработчиков MQ, не влияя на производительность.

Большое спасибо - Лей


person davy_wei    schedule 01.07.2014    source источник
comment
Это новое приложение заменит устаревшее приложение, использующее bean-компоненты, управляемые сообщениями. Устаревшее приложение использует запросы тех же клиентов с динамическими очередями replyTo и не имеет проблем с производительностью. Устаревшее приложение также не кэширует отправителей (производителей). Любые идеи?   -  person davy_wei    schedule 02.07.2014
comment
попробуйте { сеанс QueueSession = mqSession.getSession(); answerToQueue = createReplyToQueue (сеанс); отправитель = session.createSender(replyToQueue); Сообщение TextMessage = session.createTextMessage(invocation.getResponse()); message.setJMSCorrelationID(getJmsRequest().getJMSMessageID()); getLogger().logDebug(имя метода, отправка ответа на [ + replyToQueue + ]); отправитель.отправить(сообщение); getLogger().logDebug(имя метода, ответ отправлен на [ + replyToQueue + ]); setJmsResponse (сообщение); возвращение; } наконец { JmsUtil.closeJmsResource(отправитель); }   -  person davy_wei    schedule 02.07.2014
comment
Знаете, почему производитель никогда не закрывается? Вы создаете это как часть своего собственного кода или это создает мул? Если это так, возможно, стоит сообщить об этом их системе отслеживания проблем.   -  person Stephane Nicoll    schedule 02.07.2014
comment
Стефан, это не Мул не закрывает производителей, это потому, что мы использовали Spring JMS CachingConnectionFactory. Он кэширует сеанс и проксирует API-интерфейсы jms для целей кэширования (потребители, производители).   -  person davy_wei    schedule 02.07.2014
comment
Я смущен. Вам нужно закрыть сессию в какой-то момент, и это все уладит. Тот факт, что CachingConnectionFactory кэширует их, не имеет отношения к проблеме. Единственное, что это делает, — это убедиться, что вы возвращаете кэшированного производителя для вашей текущей транзакции, но вы (или Mule) несете ответственность за закрытие сеанса! (отметьте physicalClose() в этом классе)   -  person Stephane Nicoll    schedule 03.07.2014


Ответы (1)


Это очень интересный вариант использования. Однако, боюсь, нет ничего из коробки, чтобы исправить это. Есть более очевидные решения: отключить кеширование или расширить поставщика кеша Spring.

Временные очереди и производительность определенно не могут быть двумя вещами одновременно. Я бы предложил другой вариант:

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

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

person Víctor Romero    schedule 19.12.2014