Apache camel 使用Apache Camel ConsumerTemplate从ActiveMQ Artemis轮询字节/大消息

Apache camel 使用Apache Camel ConsumerTemplate从ActiveMQ Artemis轮询字节/大消息,apache-camel,activemq-artemis,camel-jms,Apache Camel,Activemq Artemis,Camel Jms,当通过JMS连接到ActiveMQ Artemis时,我在一个基于Apache Camel的应用程序中遇到了一个问题。在其中一条驼峰路由的末尾,消息存储在Artemis JMS队列中。在同一应用程序中运行的遗留组件使用ConsumerTemplate定期从那里提取它们 这适用于具有纯文本正文的驼峰消息,但在使用字节数组正文时会导致错误:似乎Artemis将任何具有字节正文的消息都视为一个字符串。通过ConsumerTemplate进行接收是可行的,但一旦访问了正文或标题,就会引发如下异常: or

当通过JMS连接到ActiveMQ Artemis时,我在一个基于Apache Camel的应用程序中遇到了一个问题。在其中一条驼峰路由的末尾,消息存储在Artemis JMS队列中。在同一应用程序中运行的遗留组件使用
ConsumerTemplate
定期从那里提取它们

这适用于具有纯文本正文的驼峰消息,但在使用字节数组正文时会导致错误:似乎Artemis将任何具有字节正文的消息都视为一个字符串。通过
ConsumerTemplate
进行接收是可行的,但一旦访问了正文或标题,就会引发如下异常:

org.apache.camel.RuntimeCamelException: Failed to extract body due to: javax.jms.IllegalStateException: AMQ119023: The large message lost connection with its session, either because of a rollback or a closed session. Message: ActiveMQMessage[ID:90c4d1d5-3233-11ea-b0cc-44032c68a56f]:PERSISTENT/ClientLargeMessageImpl[messageID=2974, durable=true, address=mytest,userID=90c4d1d5-3233-11ea-b0cc-44032c68a56f,properties=TypedProperties[firedTime=Wed Jan 08 17:26:03 CET 2020,__AMQ_CID=90b4f34e-3233-11ea-b0cc-44032c68a56f,breadcrumbId=ID-NB045-evolit-co-at-1578500762151-0-1,_AMQ_ROUTING_TYPE=1,_AMQ_LARGE_SIZE=3]]
        at org.apache.camel.component.jms.JmsBinding.extractBodyFromJms(JmsBinding.java:172) ~[camel-jms-2.22.1.jar:2.22.1]
        at org.apache.camel.component.jms.JmsMessage.createBody(JmsMessage.java:221) ~[camel-jms-2.22.1.jar:2.22.1]
        at org.apache.camel.impl.MessageSupport.getBody(MessageSupport.java:54) ~[camel-core-2.22.1.jar:2.22.1]
        at org.apache.camel.example.cdi.JmsPoller.someMethod(JmsPoller.java:36) ~[classes/:?]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_171]
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_171]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_171]
        at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_171]
        at org.apache.camel.component.bean.MethodInfo.invoke(MethodInfo.java:481) ~[camel-core-2.22.1.jar:2.22.1]
        at org.apache.camel.component.bean.MethodInfo$1.doProceed(MethodInfo.java:300) ~[camel-core-2.22.1.jar:2.22.1]
        at org.apache.camel.component.bean.MethodInfo$1.proceed(MethodInfo.java:273) ~[camel-core-2.22.1.jar:2.22.1]
        at org.apache.camel.component.bean.AbstractBeanProcessor.process(AbstractBeanProcessor.java:188) ~[camel-core-2.22.1.jar:2.22.1]
        at org.apache.camel.component.bean.BeanProcessor.process(BeanProcessor.java:53) ~[camel-core-2.22.1.jar:2.22.1]
        at org.apache.camel.component.bean.BeanProducer.process(BeanProducer.java:41) ~[camel-core-2.22.1.jar:2.22.1]
        at org.apache.camel.processor.SendProcessor.process(SendProcessor.java:148) ~[camel-core-2.22.1.jar:2.22.1]
        at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:548) [camel-core-2.22.1.jar:2.22.1]
        at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:201) [camel-core-2.22.1.jar:2.22.1]
        at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:201) [camel-core-2.22.1.jar:2.22.1]
        at org.apache.camel.component.timer.TimerConsumer.sendTimerExchange(TimerConsumer.java:197) [camel-core-2.22.1.jar:2.22.1]
        at org.apache.camel.component.timer.TimerConsumer$1.run(TimerConsumer.java:79) [camel-core-2.22.1.jar:2.22.1]
        at java.util.TimerThread.mainLoop(Timer.java:555) [?:1.8.0_171]
        at java.util.TimerThread.run(Timer.java:505) [?:1.8.0_171]
Caused by: javax.jms.IllegalStateException: AMQ119023: The large message lost connection with its session, either because of a rollback or a closed session
        at org.apache.activemq.artemis.core.client.impl.LargeMessageControllerImpl.saveBuffer(LargeMessageControllerImpl.java:273) ~[artemis-core-client-2.6.2.jar:2.6.2]
        at org.apache.activemq.artemis.core.client.impl.ClientLargeMessageImpl.saveToOutputStream(ClientLargeMessageImpl.java:115) ~[artemis-core-client-2.6.2.jar:2.6.2]
        at org.apache.activemq.artemis.jms.client.ActiveMQMessage.saveToOutputStream(ActiveMQMessage.java:853) ~[artemis-jms-client-2.6.2.jar:2.6.2]
        at org.apache.activemq.artemis.jms.client.ActiveMQMessage.setObjectProperty(ActiveMQMessage.java:693) ~[artemis-jms-client-2.6.2.jar:2.6.2]
        at org.apache.camel.component.jms.JmsBinding.createByteArrayFromBytesMessage(JmsBinding.java:251) ~[camel-jms-2.22.1.jar:2.22.1]
        at org.apache.camel.component.jms.JmsBinding.extractBodyFromJms(JmsBinding.java:163) ~[camel-jms-2.22.1.jar:2.22.1]
        ... 21 more
Caused by: org.apache.activemq.artemis.api.core.ActiveMQIllegalStateException: AMQ119023: The large message lost connection with its session, either because of a rollback or a closed session
        at org.apache.activemq.artemis.core.client.impl.LargeMessageControllerImpl.saveBuffer(LargeMessageControllerImpl.java:273) ~[artemis-core-client-2.6.2.jar:2.6.2]
        at org.apache.activemq.artemis.core.client.impl.ClientLargeMessageImpl.saveToOutputStream(ClientLargeMessageImpl.java:115) ~[artemis-core-client-2.6.2.jar:2.6.2]
        at org.apache.activemq.artemis.jms.client.ActiveMQMessage.saveToOutputStream(ActiveMQMessage.java:853) ~[artemis-jms-client-2.6.2.jar:2.6.2]
        at org.apache.activemq.artemis.jms.client.ActiveMQMessage.setObjectProperty(ActiveMQMessage.java:693) ~[artemis-jms-client-2.6.2.jar:2.6.2]
        at org.apache.camel.component.jms.JmsBinding.createByteArrayFromBytesMessage(JmsBinding.java:251) ~[camel-jms-2.22.1.jar:2.22.1]
        at org.apache.camel.component.jms.JmsBinding.extractBodyFromJms(JmsBinding.java:163) ~[camel-jms-2.22.1.jar:2.22.1]
        ... 21 more
在测试程序中,如果消息不超过Artemis的
minLargeMessageSize
,甚至不超过3个字节,也会出现此问题

巧合的是,同样的问题发生在用于测试应用程序的独立应用程序中。在那里,我能够通过保持JMS会话和接收器打开直到完全读取JMS消息体和消息头来解决这个问题。对于Camel,它是在Camel所基于的Spring
JmsTemplate
中抽象出来的


我查阅了,以找到可能对我有帮助的配置选项。我尝试了以下方法:

  • 急切地加载fproperties=true
    在消费者方面:没有效果,似乎只会影响
    MessageListenerContainer
    。文件说:
它使用[…]Spring的JmsTemplate进行发送,使用MessageListenerContainer进行消费

然而,在调试过程中,似乎只有在骆驼路由中使用来自JMS端点的消息时才使用
MessageListenerContainer
。使用
ConsumerTemplate
就像在我的例子中一样,使用
JmsTemplate
进行消费

  • 消费者端的
    messageConverter
    mapJmsMessage
    无效,它们在会话已关闭时执行
  • alwaysCopyMessage
    在制作人方面:我想复制可能会阻止使用流式大消息,没有效果
  • streamMessageTypeEnabled=false
    生产者端:无效
  • jmsMessageType=Bytes
    在生产者和消费者端:无效
  • transferxchange=true
    在生产者和消费者方面:这似乎解决了我的具体问题,但感觉像是一个解决办法。文档建议谨慎使用该选项
所以现在,
transferExchange
似乎是我最好的选择,假设它真的解决了我在所有测试用例中的问题。尽管如此,我还是很高兴能够更好地理解这个问题或不同的解决方案:

  • 为什么Artemis将小字节数组消息视为大消息
  • Camel ConsumerTemplate是否支持流式大消息
  • 我的版本是Camel 2.22.1和Artemis 2.10.1


    我已经能够通过修改Camel发行包中的Camel示例
    Camel示例cdi
    来重现我的问题,使其具有如下所示的最小类。 此外,我还添加了
    camel jms
    和Artemis依赖项,并在本地启动了Artemis,这两个都与
    camel示例Artemis large messages
    示例中描述的一样

    公共类MyRoutes扩展RouteBuilder{
    @凌驾
    public void configure(){
    setupJmsComponent();
    从(“计时器:writeTimer?周期=6000”)
    .log(“写入JMS”)
    .setBody(()->新字节[]{0,1,2})
    .to(JmsPoller.ENDPOINT);
    从(“计时器:轮询计时器?周期=3000”)
    。致(“bean:jmsPoller”);
    }
    私有void setupJmsComponent(){
    ActiveMQJMSConnectionFactory connectionFactory=新的ActiveMQJMSConnectionFactory(“tcp://localhost:61616");
    JmsComponent JmsComponent=新JmsComponent();
    jmsComponent.setConnectionFactory(connectionFactory);
    getContext().addComponent(“jms”,jmsComponent);
    }
    }
    
    @Singleton
    @命名(“jmsPoller”)
    公共类JmsPoller{
    静态最终字符串ENDPOINT=“jms:queue:mytest”;
    @注入
    私人消费者模板消费者模板;
    公共方法(字符串体){
    Exchange=consumerTemplate.receive(端点,1000L);
    System.out.println(“接收”+(exchange==null?null:exchange.getIn().getBody());
    }
    }
    
    ActiveMQ Artemis不会将任何带有字节体的消息视为“大”消息。值得注意的是,代理最终将所有消息体视为一个字节数组,因为它们正是字节数组。然而,为了被认为是“大的”,消息必须超过一定的大小。国家:

    任何大于一定大小的消息都被视为大消息。大型邮件将被拆分并以片段形式发送。这由URL参数
    minLargeMessageSize
    确定

    注意

    Apache ActiveMQ Artemis消息使用每个字符2个字节进行编码,因此,如果消息数据用ASCII字符(1个字节)填充,则生成的Apache ActiveMQ Artemis消息的大小将大约加倍。在计算“大”消息的大小时,这一点很重要,因为它在发送之前可能看起来小于
    minLargeMessageSize
    ,但一旦编码,它就会变成“大”消息

    默认值为100KiB

    看起来应用程序的用例根本不符合ActiveMQ Artemis中大消息支持的语义,因为消息所来自的会话在消息主体完全接收之前就已关闭

    因此,我建议您在读取正文之前保持会话打开,或者在发送mes的应用程序的URL上增加
    minLargeMessageSize