JmsTransactionManager事务:Spring JMS事务类型
- Session管理的事务-原生事务
- 外部管理的事务-JmsTransactionManager、JTA
Srping JMS事务机制过程
session原生事务
代码测试
pom.xml:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-activemq</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
Receiver:
@Component public class Receiver { @Autowired private JmsTemplate jmsTemplate; @JmsListener(destination = "test.queue.first") public void receive(String msg){ System.out.println("收到的消息:" + msg); jmsTemplate.convertAndSend("test.queue.reply", "reply:" + msg); //发送消息包含error时抛出异常 if(msg.contains("error")){ throw new RuntimeException("data error!!!!!!!!!!!!!!!!!1"); } } }
测试消息发送接口:
@RestController public class DemoController { @Autowired private JmsTemplate jmsTemplate; @Autowired private Receiver receiver; /** * 通过@JmsListener监听消息并发送出去 */ @RequestMapping("/send") public void send(String msg) { jmsTemplate.convertAndSend("test.queue.first", msg); } /** * 直接发送 */ @RequestMapping("/direct") public void direct(String msg) { receiver.receive(msg); } }
测试结果:
- 浏览器输入http://localhost:8080/send?msg=data_error,通过@JmsListener监听消息并发送出去,抛出异常时,test.queue.reply没有收到消息,消息进入ActiveMQ.DLQ,查看控制台,发现消息发送了7次,这是activemq默认的重发次数
- 浏览器输入http://localhost:8080/direct?msg=data_error,直接向test.queue.reply中发送消息,消息发送成功,查看控制台报了一次错,消息并未回滚
结论:通过@JmsListener监听的方法受session事务控制,抛出异常时,无论是消息的消费还是生产均能够回滚,默认尝试7次后进入死信队列
如果直接调用该方法,不存在事务。
问题:为什么没有配置事务,会存在事务?
查看官方文档
查看 DefaultJmsListenerContainerFactoryConfigurer类源码,在configure方法中有这么一段,有外部事务的时候使用外部事务,没有的话使用session本地事务
如果使用自己配置的JmsListenerContainerFactory ,需添加factory.setSessionTransacted(true)这一行代码
// 这个用于设置 @JmsListener使用的containerFactory @Bean public JmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory){ DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory (); factory.setSessionTransacted(true); factory.setConnectionFactory(connectionFactory); factory.setReceiveTimeout(1000L); //重连间隔时间 return factory; }
JmsTransactionManager事务
修改上例代码:
添加JmsConfig
@EnableJms @Configuration public class JmsConfig { //这个用于设置 @JmsListener使用的containerFactory @Bean public JmsListenerContainerFactory<?> msgFactory(ConnectionFactory connectionFactory, DefaultJmsListenerContainerFactoryConfigurer configurer, PlatformTransactionManager transactionManager) { DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); factory.setTransactionManager(transactionManager); factory.setReceiveTimeout(10000L); configurer.configure(factory, connectionFactory); return factory; } @Bean public JmsTemplate jmsTemplate(ConnectionFactory connectionFactory){ JmsTemplate jmsTemplate = new JmsTemplate(); jmsTemplate.setConnectionFactory(connectionFactory); // jmsTemplate.setSessionTransacted(true); return jmsTemplate; } /** * JmsTransactionManager事务管理 */ @Bean public PlatformTransactionManager transactionManager(ConnectionFactory connectionFactory) { return new JmsTransactionManager(connectionFactory); } }
Receiver:
@Component public class Receiver { @Autowired private JmsTemplate jmsTemplate; @Transactional @JmsListener(destination = "test.queue.first") public void receive(String msg){ System.out.println("收到的消息:" + msg); jmsTemplate.convertAndSend("test.queue.reply", "reply:" + msg); //发送消息包含error时抛出异常 if(msg.contains("error")){ throw new RuntimeException("data error!!!!!!!!!!!!!!!!!1"); } } }
同上测试,过程略:
结论:两种调用方法都受到事务控制。
通过@JmsListener监听消息进行调用会rollback,进入死信队列
直接调用不会进入队列(受到@Transactional的事务控制)
参考:https://blog.csdn.net/songhaifengshuaige/article/details/54177339
https://blog.csdn.net/songhaifengshuaige/article/details/54177242
http://elim.iteye.com/blog/1983532