spring-jms事务

 

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

https://docs.spring.io/spring-boot/docs/1.5.17.RELEASE/reference/htmlsingle/#boot-features-using-jms-receiving

http://elim.iteye.com/blog/1983532

 

posted @ 2018-11-02 16:18  gloomysun  阅读(1430)  评论(0编辑  收藏  举报