concurrentConsumers>1时Camel路由无法接收外部Artemis JMS消息的咨询
JBoss EAP 7.4.2中Camel JMS并发消费者配置失败问题
在JBoss EAP 7.4.2中配置Camel路由,从3台支持HA/故障转移的外部ActiveMQ Artemis 2.16服务器并发接收JMS消息时,设置concurrentConsumers=2或更大值会出现以下警告并导致失败:
2023-xx-xx 16:04:37,066 GMT WARN [org.apache.camel.component.jms.DefaultJmsMessageListenerContainer] (Camel (StandardContext) thread #12 - JmsConsumer[XXXQueue]) Setup of JMS message listener invoker failed for destination 'XXXQueue' - trying to recover. Cause: Only allowed one session per connection. See the J2EE spec, e.g. J2EE1.4 Section 6.6
Camel路由代码
from("jms:queue:XXX?concurrentConsumers=2") // 或更大值 .process("xyz") .end();
环境信息
- Apache Camel 2.25.4
- 连接3台外部ActiveMQ Artemis 2.16服务器,支持故障转移/HA
- 使用默认协议连接
ActiveMQ Artemis资源适配器配置
<subsystem xmlns="urn:jboss:domain:messaging-activemq:13.0"> <remote-connector name="netty-artemis-sharedinternal-1" socket-binding="artemis-sharedinternal-1"/> <remote-connector name="netty-artemis-sharedinternal-2" socket-binding="artemis-sharedinternal-2"/> <remote-connector name="netty-artemis-sharedinternal-3" socket-binding="artemis-sharedinternal-3"/> <pooled-connection-factory name="org.apache.activemq" entries="java:/ConnectionFactory" connectors="netty-artemis-sharedinternal-1 netty-artemis-sharedinternal-2 netty-artemis-sharedinternal-3" ha="true" failover-on-initial-connection="true" use-topology-for-load-balancing="true" transaction="none" user="user" password="password" min-pool-size="24" use-auto-recovery="true" max-pool-size="256" initial-connect-attempts="2" statistics-enabled="true" enable-amq1-prefix="false"> <inbound-config use-jndi="false" rebalance-connections="true" setup-attempts="2" setup-interval="5000"/> </pooled-connection-factory> ... </subsystem>
问题解答
1. 规避警告/失败的解决方案
问题根源是JCA规范限制单个连接只能创建一个会话,而Camel默认会在同一个连接上为多个并发消费者创建多个会话,触发了规范限制。可以通过以下方案解决:
- 修改Camel JMS组件参数
在路由的JMS端点中添加useSingleConnectionPerConsumer=true参数,让每个并发消费者使用独立的连接,避免单个连接下创建多个会话:
该参数会让每个消费者从连接池获取独立连接,既符合JCA规范,又能满足并发消费需求。同时确保连接池的from("jms:queue:XXX?concurrentConsumers=2&useSingleConnectionPerConsumer=true") .process("xyz") .end();min-pool-size设置大于等于并发消费者数量,避免连接池不足导致共享连接。
2. 在JBoss中创建非JCA兼容的Artemis连接工厂
可以创建非JCA兼容的原生Artemis连接工厂,有两种可行方式:
代码中直接创建原生连接工厂
使用Artemis客户端的ActiveMQConnectionFactory,直接配置包含所有HA节点的broker URL,然后注入到Camel的JMS组件中:ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory( "tcp://artemis-sharedinternal-1:61616,tcp://artemis-sharedinternal-2:61616,tcp://artemis-sharedinternal-3:61616?ha=true&failoverOnInitialConnection=true" ); factory.setUser("user"); factory.setPassword("password"); // 将工厂注册到Camel上下文 JmsComponent jmsComponent = new JmsComponent(); jmsComponent.setConnectionFactory(factory); camelContext.addComponent("jms", jmsComponent);这种方式绕开JBoss的JCA容器管理,使用原生连接工厂,不受JCA规范的单连接单会话限制。
在JBoss中注册原生连接工厂到JNDI
- 确保JBoss中已部署Artemis客户端模块(JBoss EAP 7.4默认已包含)。
- 在
standalone.xml或domain.xml中添加JNDI绑定,直接注册原生连接工厂:<subsystem xmlns="urn:jboss:domain:naming:2.0"> <bindings> <object-factory name="java:/NonJCAConnectionFactory" module="org.apache.activemq.artemis" class="org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory"> <environment> <property name="brokerURL" value="tcp://artemis-sharedinternal-1:61616,tcp://artemis-sharedinternal-2:61616,tcp://artemis-sharedinternal-3:61616?ha=true&failoverOnInitialConnection=true"/> <property name="user" value="user"/> <property name="password" value="password"/> </environment> </object-factory> </bindings> </subsystem> - 在Camel路由中引用该JNDI连接工厂:
from("jms:queue:XXX?concurrentConsumers=2&connectionFactory=#NonJCAConnectionFactory") .process("xyz") .end();
内容的提问来源于stack exchange,提问作者Surjit Sen
相关产品推荐
相关产品推荐

