#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. Если это правильный ответ, вы должны отметить его как таковой. Спасибо!