• 学习ActiveMQ(七):JMS消息的事务管理


    Spring提供了一个JmsTransactionManager用于对JMS ConnectionFactory做事务管理。这将允许JMS应用利用Spring的事务管理特性。JmsTransactionManager在执行本地资源事务管理时将从指定的ConnectionFactory绑定一个ConnectionFactory/Session这样的配对到线程中。JmsTemplate会自动检测这样的事务资源,并对它们进行相应操作。 

    在Spring整合JMS的应用中,如果我们要进行本地的事务管理的话非常简单,只需要在定义对应的消息监听容器时指定其sessionTransacted属性为true。

    xml: 增加<property name="sessionTransacted" value="true"/>

    <!--配置 消息监听容器-->
        <bean id="jmsContainer" class=" org.springframework.jms.listener.DefaultMessageListenerContainer">
            <property name="connectionFactory" ref="connectionFactory"/>
            <property name="destination" ref="queueDestination"/>
            <property name="messageListener" ref="consumerMessageListener"/>
            <!--应答模式是 INDIVIDUAL_ACKNOWLEDGE-->
            <!--AUTO_ACKNOWLEDGE = 1    自动确认
            CLIENT_ACKNOWLEDGE = 2    客户端手动确认
            DUPS_OK_ACKNOWLEDGE = 3    自动批量确认
            SESSION_TRANSACTED = 0    事务提交并确认
            INDIVIDUAL_ACKNOWLEDGE = 4    单条消息确认-->
            <property name="sessionAcknowledgeMode" value="4"/>
            <!--对消息开启事务模式-->
            <property name="sessionTransacted" value="true"/>
        </bean>

     监听器加入session.rollback();消息进行回滚重传(上一篇中因为还没有学习回滚,所以用的session.recover()实现的重发,现在就可以使用事务rollback()方法实现了)

    回滚的过程是消息先出列,然后重发,默认6次,超过次数后进入到死亡队列,(配置持久化数据库的时候,并持久化到数据库一条数据);

    回滚肯定是开启了事务的情况下,那么没有开启事务的情况呢?消息没有确认的情况,消息会停在消息队列中,等待着再次被监听,除非调用session.recover()方法,效果和开启事务并回滚一样会进入死亡队列。

       session.rollback();//手动的调用此方法进行回滚,抛出异常时实际上事务开启后会自动进行回滚的。

    调用commit()方法进行提交:值得说的是,在事务模式下,在接收消息没有确认的情况也会出列。完成消息。

     session.commit();

    也就是说,开启事务后消息永远不会出现停留在队列的情况,消息会回滚重发,最后到死亡队列中,而不开启事务的情况,只要不使用session.recover();消息会停留在队列中,不会重发,直至被确认出列。如果调用了recover就和回滚重发一样了。

    我们在实际业务中,接收消息操作和数据库操作要在一起进行那么该怎么控制事务呢?

    Spring里,如果同时存在JMS操作和DB操作,大概也就三种方式:

    1.没有使用JTA。JMS不在事务中,DB操作在事务中   

         a,消息处理

         b,开始数据库事务

         c,数据库操作

         d,数据库提交

             成功:结束

             失败:回到b重试

    这种方式事务没有集成,靠的纯粹是我们程序的控制,如果最终数据库提交都没成功的话,可以记下log,再人工去纠正数据。例子里把数据库操作放在了更重要的位置,其实也可以倒过来,让数据库操作先完成,只好在做jms操作,看业务需求了:

         a,开始数据库事务

         b,数据库操作

         c,数据库提交

         d,消息处理

             成功:结束

             失败:回到d重试

    我们只要把spring配置改成

    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
    ...
    <!-- This is important... -->
    <property name="sessionTransacted" value="false" />
    </bean> 
    再加上自己程序控制就好了。

    2.如果没有使用JTA,对于上面的配置如果把属性 sessionTransacted 设成true的话,就产生了第二种方式,过程:

        a,开始jms事务

        b,开始数据库事务

        c,数据库和jms操作

        d,提交数据库操作

        e,提交jms操作

    a,b的顺序不肯定,不过对程序没影响,但是d,e的顺序是确定的。在Spring里,数据库会在spring的commit方法里提交,而JMS会在afterCommit方法里提交。如果数据库失败,JMS当然也就回滚了,但是如果数据库成功而JMS失败,就产生了数据不一致,就要加上其它措施。而且这里还有一个致命缺点,就是某些情况下即使JMS失败(比如JMS服务器down了),Spring也不会抛出异常,程序外部以为一切正常,而事实上以产生了不一致问题,而且很难发现。

    3.如果使用了JTA那么把属性 sessionTransacted 设成true的话,

    JMS和数据库操作就在同一个事务里了,没什么好说,最安全的方式,但是效率很低。

      

  • 相关阅读:
    DockerPush
    DockerInstall
    DockerFile
    基于虚拟机实例/java程序线程的虚拟机内存分配
    Class文件结构及方法中的指令
    JAVA类型生命周期的开始阶段和使用阶段/以及创建对象的几种方式
    。。。。。毕业季
    PCA算法
    ffmpeg将图片转为视频
    Linux下使用bgslibrary的OpenCv库
  • 原文地址:https://www.cnblogs.com/liuyuan1227/p/10776384.html
Copyright © 2020-2023  润新知