学习spring事务与消息队列_java

在开发过程中,遇到一个bug,产生bug的原因是spring事务提交晚于消息队列的生产消息,导致消息队列消费消息时获取到的数据不正确。这篇文章介绍问题的产生和一步步的解决过程。

一.问题的产生:

场景还原:接口中的一个方法,首先修改订单状态,然后向消息队列中生产消息,消息队列的消费者获取到消息检测订单状态,发现订单状态未更改。

代码:

@Service(orderApi)
public class OrderApiImpl implements OrderApi {
  @Resource MqService mqService;
  @OrderDao orderDao;

  public void push(String orderId) {
    // 更新订单状态,之前的状态是1
    updateStatus(orderId, 3);
    // 产生消息
    mqService.produce(orderId);
  }
  public viod updateStatus(String orderId, Integer status) {
    orderDao.updateStatus(orderId, status);
  }
}

问题产生原因:orderApi中的所有方法都有事务,事务类型PROPAGATION_REQUIRED,所以push方法对数据的操作会在push代码全部执行之后提交,而在事务提交之前消息队列的消息已经产生所以消息队列中消费到的订单从数据库查询出的状态可能还为1。为了让bug现象更明显,可以在push方法最后添加:

try {
  Thread.sleep(10000);
} catch (InterruptedException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
}

这样就会发现消费消息时,订单状态一定是未修改的。 

二.问题的解决:

解决方案:在更新数据时,新建一个事物,保证更新代码执行完成后,更新数据库的事务已被提交。(确保消息产生前数据库操作已提交)

按照上述方案,我首先想到的是直接修改updateStatus方法的事务类型;我将此方法的事务类型改为PROPAGATION_REQUIRES_NEW(新建事务,如果当前存在事务,把当前事务挂起)。

但是这么做有两点不合适:

  1.强制修改了updateStaus的事务类型,可能影响其他流程。

  2.未起到作用,updateStaus方法中没有新建事务。

关于第二点的解释:spring添加事务是通过BeanNameAutoProxyCreator实现的动态代理,只是给bean对象添加了事务,现在在类内部调用方法,是不会触发新事物的创建的。

所以在经过以上尝试后,我创建了一个新的类:

@Service("orderExtApi")
public class OrderExtApiImpl {
  @Resource OrderApi orderApi;

  public void updateStatusNewPropagation(String orderId) {
    orderApi.updateStatus(orderId);
  }
}

并为updateStatusNewPropagation方法添加事务PROPAGATION_REQUIRES_NEW

这个类就只是为了给orderApi中的updateStaus方法新起一个事务。

ok,到此为止bug已经解决了。

但是代码中还是存在问题:对数据库的操作已经提交,如果生产消息出现异常对业务逻辑来说还是错误的。所以需要检测消息的产生是否完成。

最终orderApi中的代码如下:

@Service(orderApi)
public class OrderApiImpl implements OrderApi {
  @Resource MqService mqService;
  @Resource OrderDao orderDao;
  @Resource OrderExtApiImpl orderExtApi;

  public void push(String orderId) {
    // 更新订单状态,之前的状态是1
    orderExtApi.updateStatusNewPropagation(orderId, 3);
    // 产生消息--produce会检测是否出现异常 当返回1时表示生产消息成功
    Response response = mqService.produce(orderId);
    if (response.getCode() != 1) {
      log.info("消息队列生产消息异常:" + response.getErrorMsg())
      // 生产消息异常,重置状态 等待下次重新执行
      orderExtApi.updateStatusNewPropagation(orderId, 1);
    }

  }
  public viod updateStatus(String orderId, Integer status) {
    orderDao.updateStatus(orderId, status);
  }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索spring
, 事务
消息队列
,以便于您获取更多的相关知识。

时间: 2024-09-26 23:24:02

学习spring事务与消息队列_java的相关文章

Spring学习笔记3之消息队列(rabbitmq)发送邮件功能_java

rabbitmq简介: MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们.消息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术.排队指的是应用程序通过 队列来通信.队列的使用除去了接收和发送应用程序同时执行的要求.其中较为成熟的MQ产品有IBM WEBSPHERE MQ. 本节的内容是用户注册时,将邮

spring mvc redis消息队列

通常情况下,为了提高系统开发的灵活性和可维护度,我们会采用消息队列队系统进行解耦.下面是一个采用spring redis实现的消息队列实例,但此实例会由于网络延迟和阻塞等情况导致消息处理的延时,因而不能确保消息处理的顺序,所以使用时需要注意. pom.xml中添加如下配置 添加版本配置 <properties> <jedis.version>2.8.1</jedis.version> <spring-data-redis.version>1.7.2.RELE

Linux进程间通信学习:如何使用消息队列

下面来说说如何用不用消息队列来进行进程间的通信,消息队列与命名管道有很多相似之处.有关命名管道的更多内容可以参阅我的另一篇文章:Linux进程间通信--使用命名管道 一.什么是消息队列 消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法.  每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构.我们可以通过发送消息来避免命名管道的同步和阻塞问题.但是消息队列与命名管道一样,每个数据块都有一个最大长度的限制. Linux用宏MSGMAX和MSGMNB来限制一条消息

求教多线程接收消息队列的问题

问题描述 写了个服务,用多线程来接收消息队列,但是每次测试20万条数据,接收过来都要少几百条数据,百思不得其解--我的业务逻辑:1.定期扫描msmq2.如果有消息,开多线程循环接收3.每个线程接收到消息后放在内存里4.等所有线程结束,用bulkcopy入库现在问题出在第3步,假设消息队列里有20万条数据,每次线程全部结束后内存里只有19万9千条左右,总要少1000条左右--请问有什么可能导致这个问题?下面是代码///<summary>///间隔一定时间(ExecuteTime)调用Receiv

分布式系统解决之道:目录、消息队列、事务系统及其他

目录服务(ZooKeeper)   分布式系统是一个由很多进程组成的整体,这个整体中每个成员部分,都会具备一些状态,比如自己的负责模块,自己的负载情况,对某些数据的掌握等等.而这些和其他进程相关的数据,在故障恢复.扩容缩容的时候变得非常重要.   简单的分布式系统,可以通过静态的配置文件,来记录这些数据:进程之间的连接对应关系,它们的IP地址和端口等等.然而,一个自动化程度高的分布式系统,必然要求这些状态数据都是动态保存的.这样才能让程序自己去做容灾和负载均衡的工作.   一些程序员会专门自己编

几种常见的微服务架构方案——ZeroC IceGrid、Spring Cloud、基于消息队列、Docker Swarm

微服务架构是当前很热门的一个概念,它不是凭空产生的,是技术发展的必然结果.虽然微服务架构没有公认的技术标准和规范草案,但业界已经有一些很有影响力的开源微服务架构平台,架构师可以根据公司的技术实力并结合项目的特点来选择某个合适的微服务架构平台,以此稳妥地实施项目的微服务化改造或开发进程. 本文选自<架构解密:从分布式到微服务>. 本文盘点了四种常用的微服务架构方案,分别是ZeroC IceGrid.Spring Cloud.基于消息队列与Docker Swarm. ZeroC IceGrid微服

Spring事务管理只对出现运行期异常进行回滚_java

一.结论 Spring的事务管理默认只对出现运行期异常(java.lang.RuntimeException及其子类)进行回滚. 如果一个方法抛出Exception或者Checked异常,Spring事务管理默认不进行回滚. 关于异常的分类一下详细介绍: 1.基本概念 看java的异常结构图  Throwable是所有异常的根,java.lang.Throwable Error是错误,java.lang.Error Exception是异常,java.lang.Exception 2.Excep

深入理解Spring事务原理_java

一.事务的基本原理 Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的.对于纯JDBC操作数据库,想要用到事务,可以按照以下步骤进行:  1.获取连接 Connection con = DriverManager.getConnection()  2.开启事务con.setAutoCommit(true/false);  3.执行CRUD  4.提交事务/回滚事务 con.commit() / con.rollback();  5.关闭连接

Spring与Hibernate整合事务管理的理解_java

在谈Spring事务管理之前我们想一下在我们不用Spring的时候,在Hibernate中我们是怎么进行数据操作的.在Hibernate中我们每次进行一个操作的的时候我们都是要先开启事务,然后进行数据操作,然后提交事务,关闭事务,我们这样做的原因是因为Hibernate默认的事务自动提交是false,他是需要我们人为的手动提交事务,假如你不想每次都手动提交事务的话,你可以在hibernate.cfg.xml我文件中把它设置为事务自动提交: xml代码 <property name="def