Spring AMQP:发布至不存在交换机的事务异常处理疑问
问题:Spring AMQP中@Transactional包裹RabbitTemplate发送逻辑时,不存在的Exchange未抛出预期异常
我正在处理生产者向不存在的DirectExchange发送消息的场景。根据spring-amqp官方文档说明:
要在发送线程检测异常,可在RabbitTemplate上设置setChannelTransacted(true),异常会在txCommit()时被检测到。但事务会显著影响性能,因此仅为此场景启用事务前需谨慎考量。
按文档设置rabbitTemplate.setChannelTransacted(true)后,出现了不符合预期的现象:
- 当
rabbitTemplate.convertAndSend未被org.springframework.transaction.annotation.Transactional注解包裹时,能在txCommit()时抛出预期的异常; - 当该方法被
@Transactional注解包裹时,仅会静默输出错误日志,不会抛出异常。
我的业务逻辑需要先执行数据库操作,因此必须用@Transactional注解包裹发送方法,想确认这种行为是框架预期的,还是我的配置存在问题。
相关代码配置
RabbitTemplate Bean配置
@Bean RabbitTemplate rabbitTemplate(CachingConnectionFactory connectionFactory) { RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); rabbitTemplate.setReturnsCallback(new ReturnsCallback() { @Override public void returnedMessage(ReturnedMessage returned) { //routingKey doesn't exist, notify and send to parking lot... } }); rabbitTemplate.setMandatory(true); rabbitTemplate.setChannelTransacted(true); rabbitTemplate.setExchange("some.exchange"); return rabbitTemplate; }
消息发布方法
@Override @Transactional public void publishMessage() { // database processing... rabbitTemplate.convertAndSend("myRoutingKey", "myMessage"); }
现象详情
带@Transactional注解时的日志输出
ERROR o.s.a.r.c.CachingConnectionFactory [AbstractConnectionFactory.java:748] Shutdown Signal: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'some.exchange' in vhost '/consume', class-id=60, method-id=40)
不带@Transactional注解时的异常栈
Caused by: org.springframework.amqp.AmqpIOException: java.io.IOException at org.springframework.amqp.rabbit.connection.RabbitUtils.commitIfNecessary(RabbitUtils.java:144) at org.springframework.amqp.rabbit.core.RabbitTemplate.doSend(RabbitTemplate.java:2398) at org.springframework.amqp.rabbit.core.RabbitTemplate.lambda$send$3(RabbitTemplate.java:1064) at org.springframework.amqp.rabbit.core.RabbitTemplate.invokeAction(RabbitTemplate.java:2229) at org.springframework.amqp.rabbit.core.RabbitTemplate.doExecute(RabbitTemplate.java:2188) at org.springframework.amqp.rabbit.core.RabbitTemplate.execute(RabbitTemplate.java:2140) at org.springframework.amqp.rabbit.core.RabbitTemplate.send(RabbitTemplate.java:1063) at org.springframework.amqp.rabbit.core.RabbitTemplate.convertAndSend(RabbitTemplate.java:1128) at org.springframework.amqp.rabbit.core.RabbitTemplate.convertAndSend(RabbitTemplate.java:1110) at gov.osc.lgmm.service.core.impl.CoreServiceImpl.sendFilingStatus(CoreServiceImpl.java:120) ... 75 common frames omitted Caused by: java.io.IOException: null at com.rabbitmq.client.impl.AMQChannel.wrap(AMQChannel.java:129) at com.rabbitmq.client.impl.AMQChannel.wrap(AMQChannel.java:125) at com.rabbitmq.client.impl.AMQChannel.exnWrappingRpc(AMQChannel.java:147) at com.rabbitmq.client.impl.ChannelN.txCommit(ChannelN.java:1540) at com.rabbitmq.client.impl.ChannelN.txCommit(ChannelN.java:46) at org.springframework.amqp.rabbit.connection.PublisherCallbackChannelImpl.txCommit(PublisherCallbackChannelImpl.java:633) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:90) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:55) at java.lang.reflect.Method.invoke(Method.java:508) at org.springframework.amqp.rabbit.connection.CachingConnectionFactory$CachedChannelInvocationHandler.invoke(CachingConnectionFactory.java:1157) at com.sun.proxy.$Proxy185.txCommit(Unknown Source) at org.springframework.amqp.rabbit.connection.RabbitUtils.commitIfNecessary(RabbitUtils.java:141) ... 84 common frames omitted Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'some.exchange' in vhost '/consume', class-id=60, method-id=40) at com.rabbitmq.utility.ValueOrException.getValue(ValueOrException.java:66) at com.rabbitmq.utility.BlockingValueOrException.uninterruptibleGetValue(BlockingValueOrException.java:36) at com.rabbitmq.client.impl.AMQChannel$BlockingRpcContinuation.getReply(AMQChannel.java:502) at com.rabbitmq.client.impl.AMQChannel.privateRpc(AMQChannel.java:293) at com.rabbitmq.client.impl.AMQChannel.exnWrappingRpc(AMQChannel.java:141) ... 94 common frames omitted Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'some.exchange' in vhost '/consume', class-id=60, method-id=40) at com.rabbitmq.client.impl.ChannelN.asyncShutdown(ChannelN.java:517) at com.rabbitmq.client.impl.ChannelN.processAsync(ChannelN.java:341) at com.rabbitmq.client.impl.AMQChannel.handleCompleteInboundCommand(AMQChannel.java:182) at com.rabbitmq.client.impl.AMQChannel.handleFrame(AMQChannel.java:114) at com.rabbitmq.client.impl.AMQConnection.readFrame(AMQConnection.java:739) at com.rabbitmq.client.impl.AMQConnection.access$300(AMQConnection.java:47) at com.rabbitmq.client.impl.AMQConnection$MainLoop.run(AMQConnection.java:666) ... 1 common frames omitted
环境信息
- Spring Boot版本:
2.5.2 - Spring AMQP版本:
2.3.9 - 事务管理器:
JpaTransactionManager
内容的提问来源于stack exchange,提问作者Imperious
相关产品推荐
相关产品推荐

