捕获事务异常并且回滚

前端传过来一个文件id,需要处理这个文件,这个文件有一点大,需要立即生成一个处理记录并返回给前端,状态字段为处理中;

解析这个文件,这个文件里面有很多数据,必须要事务回滚,处理成功,把刚才的处理记录状态字段改为处理成功;处理失败,回滚解析文件的数据库操作,并把异常返回,把刚才的处理记录状态字段改为处理失败,并且保存异常信息到处理记录的错误描述字段,然后把处理结果返回给前端。处理的过程可能长达几分钟或者几十分钟!

比较迷茫的是如何捕获事务的异常并且回滚,如何返回两次数据给前端,如果在回滚的情况下插入处理记录到数据库,对mq不是太熟!

从需求描述当中可以看出,文件比较大而且需要及时的响应给前端。这里我们可以使用异步来处理,接收请求与处理请求分离。可以使用消息中间件 MQ。

数据操作涉及到 MQ 异步处理,所以消息消费有可能重复,数据处理的时候要考虑到幂等。这里可以使用 Mysql 的乐观锁机制。在更新数据状态的时候带上上一个数据的状态。

MQ 数据消费有可能会出现处理异常,这个时候我们就需要系统可以自动的去驱动中间状态的数据到达最终状态。所以使用定时任务处理文件解析处理中的数据。

1、E-R 图

这里包含两个对象:一个是文件信息;一个是解析文件保留的具体数据。比如你有一个 excel 文件:

  • file_info 这个表是保存这个 excel 文件的基本信息以及解析状态。这个状态可以驱动文件解析的整个过程(正常或者异常)。
  • file_record 这个表是保存解析 excel 文件里面的每条数据的。

注意:这个表设计只是基本的核心字段,你可以根据业务进行扩充。

img

2、正常流程

在正常流程当中,服务端收到请求直接保存文件信息,然后发消息给 MQ,自己并不处理。直接返回给客户端,这个处理效率是极快的。文件的处理交给 MQ 的消费者,这样 MQ 消费就可以慢慢消费。

img

修改 file_info 的时候注意 SQL 语句为:

update file_info status = 'processing' where id = xxx and status = 'init';

可以根据修改条数来判断是否成功操作数据库,如果修改条数为 0。则抛出异常,数据就会回滚。这样才能做到幂等。

3、异常流程

有可能 MQ 进行阻塞,导致 consumer 一直没有成功监听。下面定时任务就是一个兜底方案。

img

修改 file_info 的时候注意 SQL 语句为:

update file_info status = 'success' where id = xxx and status = 'processing';

可以根据修改条数来判断是否成功操作数据库,如果修改条数为 0。则抛出异常,数据就会回滚。这样才能做到幂等。

看下这篇博客,也许你就懂了,链接:多线程实现事务回滚