我试图用Spring的JmsTemplate
发送JMS消息,但它失败了,这是堆栈跟踪中显示的根本原因:
AMQ219007: Cannot connect to server
但是,我可以使用传递给JmsTemplate
的相同连接工厂直接针对JMS类编程发送消息。我确信我错过了一些明显的东西,但我只是不知道什么配置是缺失的,使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}&password=$jmsArtemisSetup{password}&jms.prefetchPolicy.all=1&consumerWindowSize=0&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 "classic"连接工厂(即org.apache.activemq.jndi.ActiveMQInitialContextFactory
)。
这是我尝试的目标解析器配置。我使用<property name="destinationResolver" ref="artemisDestinationResolver" />
将其添加到JmsTemplate
配置:
<bean id="artemisDestinationResolver"
class="org.springframework.jms.support.destination.JndiDestinationResolver">
<property name="jndiTemplate" ref="amqJndiTemplate" />
<property name="cache" value="true" />
</bean>
错误的根本原因是应用程序试图用相同的客户端ID建立两个JMS连接到JMS服务器。这是JMS规范不允许的。
解决方案是使用连接池。通常,出于性能原因,应该包含连接池。在这种特殊情况下,应用程序启动,监听一条消息,发送一条回复消息,然后关闭,因此没有启用池。但是,当使用artemis-jms-client库时,这意味着使用相同的客户端ID创建了两个不同的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}&password=$jmsArtemisSetup{password}&consumerWindowSize=0&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-client库从2.10.1升级到2.18.0在堆栈跟踪中提供了比原来更有用的消息。javax.jms.InvalidClientIDException: clientID=Node1 was already set into another connection
使用连接池,JMS代码可以与artemis-jms-client 2.10.1或2.18.0一起工作。