我试图了解Spring Listener Container如何在重试上下文中处理事务。
我配置了这样的东西:
<rabbit:listener-container connection-factory="connectionFactory"
transaction-manager="chainedTransactionManager"
channel-transacted="true"
advice-chain="retryAdvice">
<rabbit:listener ref="myMessageProcessor" queue-names="test.messages" method="handleMessage"/>
</rabbit:listener-container>
我希望交易将包含在重试中,这样如果我的交易因任何原因失败,我可以决定重试特定的例外,而其他人只需将消息发送到我的DLQ。
但是,我很惊讶地注意到重试代码包含在事务代码中而不是相反,这似乎更合理。
换句话说,Spring监听器似乎做了:
doIntransaction -> doWithRetry -> invokeMyCode
我希望它会是这样的:
doWithRetry - doIntransaction -> invokeMyCode
我的计划是使用ChainedTransactionManager
同时包含JpaTransactionManager
和RabbitTransactionManager
来处理这两者,确认我读取的消息,以及提交我发送的消息在此交易期间,根据某些条件重试我的整个交易,但这似乎不会那样。
不仅如此,在事务中发生异常之后,上下文可能变得无用。我需要一个新的交易来重试才有意义。
并且存在这样的问题:在提交/回滚阶段期间发生的任何异常都不会被重试,因为它们发生在重试上下文之外。我假设他们只是根据ErrorHandler
配置重试,而不是基于我建议的代码。不幸的是,ErrorHandler
没有后退政策或有用的RetryContext
详细信息,用于计算我重试交易的次数。
使用事务管理器配置监听器以及在这种情况下重试功能的正确或最推荐的方法是什么?
答案 0 :(得分:1)
我还没有尝试过,但您应该能够通过从容器中删除事务管理器并将正常的Spring TransactionInterceptor
添加到建议链(在重试建议之后)来实现所需的行为
当容器有事务管理器时,你告诉它在调用监听器之前启动事务(它包含在建议链中)。
但是,您可能会在日志中产生一些噪音,因为容器可能仍会尝试确认/提交传递,因为它会认为&#34;它使用本地事务(拦截器已经完成它,如果配置了RabbitTransactionManager
)。
只要你没有在RabbitTransactionManager
中加入chainedTransactionManager
,就不会发生这种情况。容器只会使用本地事务。
如果包含RTM,则可能需要使用手动确认或向容器添加虚拟事务管理器以防止这种情况发生。
告诉我们你是如何知道的;我明天可以看看。
修改强>
如下所述,使用有状态重试是一种更简单的解决方案,因为消息被拒绝并重新传递。但是,您需要messageId
标头(或自定义密钥生成器)。