【问题标题】:JMS message redelivery on exception in JMS listenerJMS 侦听器异常时重新传递 JMS 消息
【发布时间】:2015-06-07 10:58:26
【问题描述】:

org.springframework.jms.listener.AbstractMessageListenerContainer 的 Javadoc 状态,如果

“sessionAcknowledgeMode”设置为“CLIENT_ACKNOWLEDGE”:监听执行成功后自动消息确认;抛出异常时不重新投递。

我猜,“在抛出异常的情况下不重新传递”意味着,即使 jms 侦听器中抛出异常,该消息也不会被重新传递(所以,我猜,它会被确认)。但是,监听器抛出的异常意味着对它的调用不成功,并且由于没有确认而应该重新传递。

问题是:
如果 jms 侦听器中抛出异常,消息确认实际上应该发生什么?

真正发生的事情,可以从这个堆栈跟踪中看出:

at org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:98)
at org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:66)
at org.springframework.jms.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:660)
at org.springframework.jms.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:620)
at org.springframework.jms.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:591)
at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.doReceiveAndExecute(AbstractPollingMessageListenerContainer.java:308)
at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:246)
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1142)
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1134)
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:1031)

堆栈跟踪的第 5 行特别有趣。那里的代码基本上意味着,(大部分)从侦听器抛出的任何异常都将绕过在org.springframework.jms.listener.AbstractMessageListenerContainer#commitIfNecessary 中完成的确认。
没关系,但是“在抛出异常的情况下不重新投递”是什么意思呢?

附加信息:
弹簧-jms:4.1.2

<bean id="someListenerContainerFactory" class="org.springframework.jms.config.DefaultJmsListenerContainerFactory">
    <property name="connectionFactory" ref="connectionFactory"/>
    <property name="concurrency" value="1-10"/>
    <property name="sessionAcknowledgeMode">
        <util:constant static-field="javax.jms.Session.CLIENT_ACKNOWLEDGE"/>
    </property>
</bean>

【问题讨论】:

    标签: java spring spring-jms


    【解决方案1】:

    这取决于您使用的侦听器容器;当使用自动确认模式时,SimpleMessageListenerContainer 在侦听器返回后确认(即传统的 JMS MessageListener)。 DefaultMessageListenerContainer 在监听器被调用之前确认,所以你需要acknowledgeMode="transacted" 来防止消息丢失。

    这方面的 javadocs 有点误导,一直是improved recently

    使用 CLIENT_ACKNOWLEDGE,您可以自己完成任务。那个文件只是意味着你在经纪人的心血来潮。根据JMS消息javadoc:

    已收到但未确认的消息可以重新发送

    根据我的经验,最好使用带有SMLC 的自动确认和带有DMLC 的交易。

    【讨论】:

    • 我明白了,但 CLIENT_ACKNOWLEDGE 仍然没有任何变化。我正在使用DefaultMessageListenerContainer。而且,在 javadocs 中,CLIENT_ACKNOWLEDGE 表示容器将在 成功执行侦听器后为我做 ack
    • 否; CLIENT_ACKNOWLEDGE 表示您有责任致电Message.acknowledge()。阅读 JMS 规范。
    • 我问的不是 JMS 规范,而是 spring 容器的行为。如果您查看org.springframework.jms.listener.AbstractMessageListenerContainer#commitIfNecessary,您可以清楚地看到,它会在侦听器执行后自动为我确认。这是否是正确的行为是不可能的,因为容器 javadoc 明确说明了这一点。
    • 我明白了 - 我没有意识到容器会执行客户端确认。但是,正如您所说,如果侦听器抛出异常,则 ack 不会完成。经纪人可能会或可能不会决定重新交付。我的问题是你为什么要使用客户端确认?如果您想更好地控制消费者,请使用JmsTemplate.execute()
    • 基本上,我需要文档中所述的内容:“成功侦听器执行后的自动消息确认”。这就是为什么我不需要更多地控制确认。但是最后一部分声明“在抛出异常的情况下不重新发送”确实具有误导性。因此关于SO的问题。如果这是 javadocs 中的错误,我很乐意提出问题,但如果不是,我想听听这部分的含义。
    猜你喜欢
    • 2011-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-22
    • 2020-03-29
    相关资源
    最近更新 更多