JmsTemplate не удается отправить сообщение в ActiveMQ Artemis с «AMQ219007: Не удается подключиться к серверу», но может отправлять сообщение с классами JMS

#java #spring-jms #activemq-artemis #jmstemplate

Вопрос:

Я пытаюсь отправить сообщения JMS с помощью Spring JmsTemplate , но это не удается, так как основная причина показана в трассировке стека:

 AMQ219007: Cannot connect to server
 

Однако я могу отправить сообщение, запрограммированное непосредственно против классов JMS, используя ту же фабрику соединений, в которую передается JmsTemplate . Я уверен, что упускаю что-то очевидное, но я просто не могу понять, какой конфигурации не хватает для JmsTemplate работы. Я думаю, что конфигурация ActiveMQInitialContextFactory правильная, так как я могу отправлять сообщения с помощью javax.jms.* классов. FWIW, я пытался настроить JmsTemplate с помощью распознавателя назначения, но это не помогло.

Вот конфигурация:

 <bean id="artemisJndiTemplate"
        class="org.springframework.jndi.JndiTemplate">
    <property name="environment">
        <props>
            <prop key="java.naming.factory.initial">org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory</prop>
            <prop key="java.naming.provider.url">(tcp://artemisServer1:61616,tcp://artemisServer2:61616)?user=$jmsArtemisSetup{user}amp;amp;password=$jmsArtemisSetup{password}amp;amp;jms.prefetchPolicy.all=1amp;amp;consumerWindowSize=0amp;amp;clientID=${NODENBRTAG}</prop>
        </props>
    </property>
</bean>

<!-- look up the JMS ConnectionFactory in JNDI -->
<bean id="artemisConnectionFactory"
        class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiTemplate" ref="artemisJndiTemplate" />
    <property name="jndiName" value="ConnectionFactory" />
</bean>

<bean id="qFromSvcToESB"
        class="org.springframework.jms.core.JmsTemplate">
    <property name="connectionFactory" ref="artemisConnectionFactory" />
    <property name="sessionAcknowledgeModeName" value="CLIENT_ACKNOWLEDGE" />
    <property name="sessionTransacted" value="true" />
    <property name="defaultDestination">
        <bean class="org.springframework.jndi.JndiObjectFactoryBean">
            <property name="jndiTemplate" ref="artemisJndiTemplate" />
            <property name="jndiName" value="dynamicQueues/qFromSvcToESB" />
        </bean>
    </property>
</bean>
 

Когда я отправляю сообщение с помощью JmsTemplate , я получаю следующую трассировку стека:

 2021-08-18 10:06:31,796 [ERROR] [com.example.emailassistant.msghdlr.EmailAssistantMsgHdlr]:171  - 1629306356191 - jmsTemplate.send(...) failed.
org.springframework.jms.UncategorizedJmsException: Uncategorized exception occurred during JMS processing; nested exception is javax.jms.JMSException: Failed to create session factory; nested exception is ActiveMQNotConnectedException[errorType=NOT_CONNECTED message=AMQ219007: Cannot connect to server(s). Tried with all available servers.]
        at org.springframework.jms.support.JmsUtils.convertJmsAccessException(JmsUtils.java:311) ~[spring-jms-5.2.12.RELEASE.jar:5.2.12.RELEASE]
        at org.springframework.jms.support.JmsAccessor.convertJmsAccessException(JmsAccessor.java:185) ~[spring-jms-5.2.12.RELEASE.jar:5.2.12.RELEASE]
        at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:507) ~[spring-jms-5.2.12.RELEASE.jar:5.2.12.RELEASE]
        at org.springframework.jms.core.JmsTemplate.send(JmsTemplate.java:576) ~[spring-jms-5.2.12.RELEASE.jar:5.2.12.RELEASE]
        at org.springframework.jms.core.JmsTemplate.send(JmsTemplate.java:567) ~[spring-jms-5.2.12.RELEASE.jar:5.2.12.RELEASE]
        at com.example.emailassistant.msghdlr.EmailAssistantMsgHdlr.execute(EmailAssistantMsgHdlr.java:151) ~[com.example.emailassistant.msghdlr-0.0.1-SNAPSHOT.jar:?]
        at com.example.framework2.activemq.artemis.EpmServiceHandlerAdapter.dispatchToListener(EpmServiceHandlerAdapter.java:71) ~[com.example.framework2.activemq.artemis-5.1-SNAPSHOT.jar:?]
        at com.example.framework2.AbstractEpmMsgHdlrAdapter$EpmMessageListener.onMessage(AbstractEpmMsgHdlrAdapter.java:615) ~[com.example.framework2-5.1-SNAPSHOT.jar:?]
        at org.apache.activemq.artemis.jms.client.JMSMessageListenerWrapper.onMessage(JMSMessageListenerWrapper.java:110) ~[artemis-jms-client-2.12.0.jar:2.12.0]
        at org.apache.activemq.artemis.core.client.impl.ClientConsumerImpl.callOnMessage(ClientConsumerImpl.java:1031) ~[artemis-core-client-2.12.0.jar:2.12.0]
        at org.apache.activemq.artemis.core.client.impl.ClientConsumerImpl.access$400(ClientConsumerImpl.java:50) ~[artemis-core-client-2.12.0.jar:2.12.0]
        at org.apache.activemq.artemis.core.client.impl.ClientConsumerImpl$Runner.run(ClientConsumerImpl.java:1154) ~[artemis-core-client-2.12.0.jar:2.12.0]
        at org.apache.activemq.artemis.utils.actors.OrderedExecutor.doTask(OrderedExecutor.java:42) ~[artemis-commons-2.12.0.jar:2.12.0]
        at org.apache.activemq.artemis.utils.actors.OrderedExecutor.doTask(OrderedExecutor.java:31) ~[artemis-commons-2.12.0.jar:2.12.0]
        at org.apache.activemq.artemis.utils.actors.ProcessorBase.executePendingTasks(ProcessorBase.java:65) ~[artemis-commons-2.12.0.jar:2.12.0]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
        at org.apache.activemq.artemis.utils.ActiveMQThreadFactory$1.run(ActiveMQThreadFactory.java:118) [artemis-commons-2.12.0.jar:2.12.0]
Caused by: javax.jms.JMSException: Failed to create session factory
        at org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory.createConnectionInternal(ActiveMQConnectionFactory.java:886) ~[artemis-jms-client-2.12.0.jar:2.12.0]
        at org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory.createConnection(ActiveMQConnectionFactory.java:299) ~[artemis-jms-client-2.12.0.jar:2.12.0]
        at org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory.createConnection(ActiveMQConnectionFactory.java:294) ~[artemis-jms-client-2.12.0.jar:2.12.0]
        at org.springframework.jms.support.JmsAccessor.createConnection(JmsAccessor.java:196) ~[spring-jms-5.2.12.RELEASE.jar:5.2.12.RELEASE]
        at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:494) ~[spring-jms-5.2.12.RELEASE.jar:5.2.12.RELEASE]
        ... 15 more
Caused by: org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException: AMQ219007: Cannot connect to server(s). Tried with all available servers.
        at org.apache.activemq.artemis.core.client.impl.ServerLocatorImpl.createSessionFactory(ServerLocatorImpl.java:690) ~[artemis-core-client-2.12.0.jar:2.12.0]
        at org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory.createConnectionInternal(ActiveMQConnectionFactory.java:884) ~[artemis-jms-client-2.12.0.jar:2.12.0]
        at org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory.createConnection(ActiveMQConnectionFactory.java:299) ~[artemis-jms-client-2.12.0.jar:2.12.0]
        at org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory.createConnection(ActiveMQConnectionFactory.java:294) ~[artemis-jms-client-2.12.0.jar:2.12.0]
        at org.springframework.jms.support.JmsAccessor.createConnection(JmsAccessor.java:196) ~[spring-jms-5.2.12.RELEASE.jar:5.2.12.RELEASE]
        at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:494) ~[spring-jms-5.2.12.RELEASE.jar:5.2.12.RELEASE]
        ... 15 more
 

Этот код и конфигурация ранее работали с «классической» фабрикой соединений ActiveMQ (т. е. org.apache.activemq.jndi.ActiveMQInitialContextFactory ).

Вот конфигурация распознавателя назначения, которую я пробовал. Я добавил его в JmsTemplate конфигурацию, используя <property name="destinationResolver" ref="artemisDestinationResolver" /> :

 <bean id="artemisDestinationResolver"
        class="org.springframework.jms.support.destination.JndiDestinationResolver">
    <property name="jndiTemplate" ref="amqJndiTemplate" />
    <property name="cache" value="true" />
</bean>
 

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

1. Страница о сервере сообщает, что это версия 2.10.1. Код доступен в версии 2.12.0 библиотек artemis (файлы jar).

2. Также стоит отметить, что ActiveMQ Artemis поддерживает JMS-клиент OpenWire, используемый ActiveMQ «classic», поэтому нет необходимости менять клиентские приложения на основной JMS-клиент, поставляемый вместе с Artemis.

3. Это правда, и именно так мы запускали наши сервисы JMS в течение последнего года. Однако у нас возникла проблема с отключением предварительной выборки, поэтому я изучал возможность перехода на библиотеки JMS от Artemis, которые используют core. Проблема в том, что мы динамически запускаем узлы для обработки сообщений, и первый, кто начнет, захватит большинство сообщений с помощью <prop key=»java.naming.provider.url»>failover:(tcp://artemisServer1:61616,tcp://artemisServer2:61616)?jms.watchTopicAdvisories=false,jms.prefetchPolicy.all=1,consumerWindowSize=0,clientID=${AIS_NODENBRTAG}<ключ prop=»java.naming.provider.url»></prop>

4. С библиотеками 2.18.0 я получаю «Исключение javax.jms.InvalidClientIDException: ClientID=Node1 уже был установлен в другое соединение». У меня уже есть соединение с завода, которое прослушивает сообщения JMS, поэтому зарегистрирован клиент с этим идентификатором, но я ожидаю, что смогу подключиться для отправки сообщений….

5. Не имеет значения, для чего будет использоваться соединение. Два соединения не могут одновременно использовать один и тот же идентификатор клиента. Это нарушение спецификации JMS.

Ответ №1:

Основная причина ошибки заключается в том, что приложение пытается установить два соединения JMS с сервером JMS с одним и тем же идентификатором клиента. Это не допускается спецификацией JMS.

Решение состоит в использовании пула соединений. Как правило, объединение в пул соединений должно быть включено по соображениям производительности. В этой конкретной ситуации приложение запускается, прослушивает одно сообщение, отправляет одно ответное сообщение и завершает работу, поэтому объединение в пул не было включено. Однако при использовании клиентской библиотеки artemis-jms это означало, что создавались два разных соединения JMS с одним и тем же идентификатором клиента, что приводило к сбою.

Следующая конфигурация XML с использованием объединения позволяет службе работать без ошибок:

 <bean id="artemisJndiTemplate"
        class="org.springframework.jndi.JndiTemplate">
    <property name="environment">
        <props>
            <prop key="java.naming.factory.initial">org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory</prop>
            <prop key="java.naming.provider.url">(tcp://artemisServer1:61616,tcp://artemisServer1:61616)?user=$jmsArtemisSetup{user}amp;amp;password=$jmsArtemisSetup{password}amp;amp;consumerWindowSize=0amp;amp;clientID=${NODENBRTAG}</prop>
        </props>
    </property>
</bean>

<!-- look up the JMS ConnectionFactory in JNDI -->
<bean id="artemisConnectionFactoryBase"
        class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiTemplate" ref="artemisJndiTemplate" />
    <property name="jndiName" value="ConnectionFactory" />
</bean>

<!-- A cached connection to wrap the ActiveMQ connection -->
<bean id="artemisConnectionFactory"
    class="org.springframework.jms.connection.CachingConnectionFactory">
    <property name="targetConnectionFactory">
        <ref bean="artemisConnectionFactoryBase" />
    </property>
    <property name="sessionCacheSize">
        <value>100</value>
    </property>
</bean>

<bean id="replyJmsQueue"
        class="org.springframework.jms.core.JmsTemplate">
    <property name="connectionFactory" ref="artemisConnectionFactory" />
    <property name="sessionAcknowledgeModeName" value="CLIENT_ACKNOWLEDGE" />
    <property name="sessionTransacted" value="true" />
    <property name="defaultDestination">
        <bean class="org.springframework.jndi.JndiObjectFactoryBean">
            <property name="jndiTemplate" ref="artemisJndiTemplate" />
            <property name="jndiName" value="dynamicQueues/replyJmsQueue" />
        </bean>
    </property>
</bean>
 

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

Кроме того, обновление клиентской библиотеки artemis-jms с 2.10.1 до 2.18.0 обеспечило гораздо более полезное сообщение в трассировке стека, чем оригинал.

 javax.jms.InvalidClientIDException: clientID=Node1 was already set into another connection
 

При объединении пулов соединений код JMS работает либо с artemis-jms-клиентом 2.10.1, либо с 2.18.0.

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

1. Если это правильный ответ, вы должны отметить его как таковой. Спасибо!