Java 提交期间Spring AMQP Rabbit MQ事务异常

Java 提交期间Spring AMQP Rabbit MQ事务异常,java,rabbitmq,amqp,spring-amqp,spring-rabbit,Java,Rabbitmq,Amqp,Spring Amqp,Spring Rabbit,我尝试使用Spring将事务消息写入HA RabbitMQ集群,并在单个事务中将实体写入H2数据库 我的代码适用于标准情况,我遇到的问题是在测试错误场景时产生的。如果我断开程序连接到的兔子节点的网络电缆,在提交阶段会引发异常,我希望回滚该异常,但是消息会以某种方式写入兔子队列,并且我的实体会持久化到数据库 以下是我收到的例外情况: org.springframework.amqp.AmqpException: failed to commit RabbitMQ transaction a

我尝试使用Spring将事务消息写入HA RabbitMQ集群,并在单个事务中将实体写入H2数据库

我的代码适用于标准情况,我遇到的问题是在测试错误场景时产生的。如果我断开程序连接到的兔子节点的网络电缆,在提交阶段会引发异常,我希望回滚该异常,但是消息会以某种方式写入兔子队列,并且我的实体会持久化到数据库

以下是我收到的例外情况:

org.springframework.amqp.AmqpException: failed to commit RabbitMQ transaction
    at org.springframework.amqp.rabbit.connection.RabbitResourceHolder.commitAll(RabbitResourceHolder.java:154)
    at org.springframework.amqp.rabbit.connection.ConnectionFactoryUtils$RabbitResourceSynchronization.processResourceAfterCommit(ConnectionFactoryUtils.java:256)
    at org.springframework.amqp.rabbit.connection.ConnectionFactoryUtils$RabbitResourceSynchronization.processResourceAfterCommit(ConnectionFactoryUtils.java:236)
    at org.springframework.transaction.support.ResourceHolderSynchronization.afterCommit(ResourceHolderSynchronization.java:85)
    at org.springframework.transaction.support.TransactionSynchronizationUtils.invokeAfterCommit(TransactionSynchronizationUtils.java:133)
    at org.springframework.transaction.support.TransactionSynchronizationUtils.triggerAfterCommit(TransactionSynchronizationUtils.java:121)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.triggerAfterCommit(AbstractPlatformTransactionManager.java:954)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:799)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:726)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:515)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:291)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
    at com.sun.proxy.$Proxy22.sendMessage(Unknown Source)
    at com.test.transactional.RabbitSender.run(RabbitSender.java:32)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)
Caused by: java.io.IOException
    at com.rabbitmq.client.impl.AMQChannel.wrap(AMQChannel.java:106)
    at com.rabbitmq.client.impl.AMQChannel.wrap(AMQChannel.java:102)
    at com.rabbitmq.client.impl.AMQChannel.exnWrappingRpc(AMQChannel.java:124)
    at com.rabbitmq.client.impl.ChannelN.txCommit(ChannelN.java:1163)
    at com.rabbitmq.client.impl.ChannelN.txCommit(ChannelN.java:61)
    at sun.reflect.GeneratedMethodAccessor5.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.springframework.amqp.rabbit.connection.CachingConnectionFactory$CachedChannelInvocationHandler.invoke(CachingConnectionFactory.java:704)
    at com.sun.proxy.$Proxy24.txCommit(Unknown Source)
    at org.springframework.amqp.rabbit.connection.RabbitResourceHolder.commitAll(RabbitResourceHolder.java:151)
    ... 18 more
Caused by: com.rabbitmq.client.ShutdownSignalException: connection error
    at com.rabbitmq.utility.ValueOrException.getValue(ValueOrException.java:67)
    at com.rabbitmq.utility.BlockingValueOrException.uninterruptibleGetValue(BlockingValueOrException.java:33)
    at com.rabbitmq.client.impl.AMQChannel$BlockingRpcContinuation.getReply(AMQChannel.java:361)
    at com.rabbitmq.client.impl.AMQChannel.privateRpc(AMQChannel.java:226)
    at com.rabbitmq.client.impl.AMQChannel.exnWrappingRpc(AMQChannel.java:118)
    ... 26 more
Caused by: java.io.EOFException
    at java.io.DataInputStream.readUnsignedByte(DataInputStream.java:290)
    at com.rabbitmq.client.impl.Frame.readFrom(Frame.java:95)
    at com.rabbitmq.client.impl.SocketFrameHandler.readFrame(SocketFrameHandler.java:139)
    at com.rabbitmq.client.impl.AMQConnection$MainLoop.run(AMQConnection.java:536)
这是我发送消息并写入数据库实体的代码: (在最多X条消息的循环中重复调用。如果收到异常,循环不会尝试重新发送消息)

@服务
公共类SenderServiceImpl实现SenderService{
私有静态最终记录器LOG=LoggerFactory.getLogger(SenderServiceImpl.class);
专用数据库服务数据库服务;
私有连接工厂连接工厂;
私有rabbit模板;
私有原子长消息ID;
私有最终字符串开始时间;
私有最终字符串交换;
私有最终字符串路由密钥;
@自动连线
公共发送者服务MPL(数据库服务数据库服务,
连接工厂连接工厂,
@值(“${rabbitmq.exchange}”)字符串交换,
@值(${rabbitmq.routingKey}”)字符串routingKey){
this.databaseService=databaseService;
this.connectionFactory=连接工厂;
这个。交换=交换;
this.routingKey=routingKey;
this.startTime=“”+新日期().getTime();
this.messageId=new AtomicLong(0);
}
@施工后
公共void init(){
模板=新的RabbitTemplate(connectionFactory);
template.setChannelTransactive(true);
}
@事务性(rollboor=AmqpException.class)
public void sendMessage(String messageString)引发AmqpException{
字符串messageCorrelationId=startTime+“-”+messageId.incrementAndGet();
MessageProperties=newmessageproperties();
setMessageId(messageCorrelationId);
Message Message=新消息(messageString.getBytes(),属性);
发送(交换,路由键,消息,新的相关数据(messageCorrelationId));
saveMessage(messageString,messageCorrelationId);
}
}
这里提供完整信息的是我的DatabaseService实现

@服务
公共类DatabaseServiceImpl实现DatabaseService{
私有TestDao TestDao;
@自动连线
公共数据库ServiceImpl(TestDao TestDao){
this.testDao=testDao;
}
@凌驾
@交易的
public void saveMessage(字符串消息,字符串消息ID){
saveMessage(message,messageId);
}
}
和TestDao类

@存储库
公共类TestDao{
@持久上下文
私人实体管理者实体管理者;
public void saveMessage(字符串消息,字符串消息ID){
TestEntity实体=新的TestEntity();
实体.setMessage(消息);
entity.setMessageId(messageId);
entityManager.persist(实体);
}    
}
和我的applicationContext.xml


此测试应用程序通过HAProxy服务器连接到RabbitMQ。集群配置了3个兔子节点

只是为了重述一下问题。如果我尝试发送30000条消息,并从连接的兔子节点拔出网络电缆。应用程序将在HAProxy注意到服务器关闭的20秒左右时间内抛出几个AMQPexception

应用程序不会尝试重新发送任何失败的消息,但我的接收应用程序将接收全部30000条消息,而我的数据库将包含全部30000个实体

有人能帮我找出原因吗

更新 为了进一步澄清,这里有一个测试函数,它会盲目地循环发送消息,而不管成功与否

for(inti=0;i
作为测试设置的示例,初始状态使发送应用程序通过HAProxy连接到兔子节点1

然后,我们从HAProxy切断与兔子节点1的网络连接。HAProxy花了大约20秒才意识到它无法连接到兔子节点1。在这段时间内,AmqpException出现了——我希望这些事务能够回滚


20秒后,HAProxy将连接发送到剩余2个兔子节点中的一个,该过程继续正常进行

抱歉,您的解释不清楚这是仅在HA情况下的问题,还是仅在单个RabbitMQ节点上存在相同的问题。我想你应该有同样的问题,即使是原始的春季AMQP。我的意思是没有涉及JPA复制。你介意确认一下吗?嗨@ArtemBilan,是的,这只是HA的问题。如果我使用单个rabbit节点运行相同的测试(仍然通过HAProxy连接),我会得到一个
org.springframework.amqp.AmqpTimeoutException
org.springframework.amqp.AmqpIOException
,然后回滚事务。消息不会传递给rabbit,也不会持久化到数据库——正如您所期望的那样。我将在我的问题中添加一个更新,试图进一步澄清这个场景。。。。是曲