JMS监听器中出现异常时的JMS消息重发

4

org.springframework.jms.listener.adapter.ListenerExecutionFailedException is thrown if there is an exception thrown from the JMS listener. When sessionAcknowledgeMode is set to CLIENT_ACKNOWLEDGE, the message would still be acknowledged even if there is an exception thrown in the listener, which means that no redelivery would occur. However, if the exception is not caught and wrapped in a RuntimeException, the message would remain unacknowledged and redelivery would be attempted.

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中执行的确认操作。
没关系,但是“发生异常时不重新传递”是什么意思?

附加信息:
spring-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>
1个回答

4
这取决于你使用哪个监听器容器;当使用AUTO ack模式时,SimpleMessageListenerContainer在侦听器返回后进行确认(即传统的JMS MessageListener)。DefaultMessageListenerContainer在调用侦听器之前进行确认,因此您需要acknowledgeMode="transacted"以防止消息丢失。

该领域的javadocs有点误导人,并已经最近得到了改进

使用CLIENT_ACKNOWLEDGE,你需要自己做确认。这份文档只是意味着你要受经纪人的支配。根据JMS消息javadoc中所述:

已接收但未确认的消息可能会被重新传送

根据我的经验,最好使用SMLC的自动确认和DMLC的事务。


我明白了,但是对于CLIENT_ACKNOWLEDGE仍然没有任何改变。 我正在使用DefaultMessageListenerContainer。 根据Javadocs,CLIENT_ACKNOWLEDGE意味着容器将在成功执行监听器后为我执行确认。 - borodust
1
不是的;CLIENT_ACKNOWLEDGE 意味着您需要负责调用 Message.acknowledge()。请阅读 JMS 规范。 - Gary Russell
我问的不是JMS规范,而是Spring容器的行为。 如果您查看org.springframework.jms.listener.AbstractMessageListenerContainer#commitIfNecessary,您可以清楚地看到,在侦听器执行后,它会自动为我执行确认操作。 是否这是正确的行为并不重要,因为容器javadoc明确指出了这一点。 - borodust
我明白了 - 我没有意识到容器可以进行客户端确认。但是,正如你所说,如果监听器抛出异常,则不会执行确认操作。代理可能会决定是否重新传递消息。我的问题是为什么要使用客户端确认?如果您想对消费者有更多的控制,请使用JmsTemplate.execute() - Gary Russell
1
基本上,我需要文档中所述的内容:“_在成功执行监听器后自动消息确认_”。这就是为什么我不需要更多对确认的控制。但最后一部分说“_如果抛出异常,则不重新传递_”真的很误导人。因此,在SO上提出了问题。如果这是javadocs中的错误,我会很高兴地提交一个问题,但如果不是,我想知道这部分的含义是什么。 - borodust
使用带有AUTO确认模式的SimpleMessageListenerContainer即可获得所需的内容 - 如果您认为javadoc存在误导,请随时打开JIRA问题。 - Gary Russell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接