个人随笔
目录
三、架构设计:对RocketMQ消息登记报错和数据库事务之间关系的一些设想
2021-04-09 17:46:19

我们在程序中,往往会用RocketMQ来进行异步解耦,让比较耗时的程序,交给消费程序慢慢做,防止一下字压垮我们的数据库。那么我们什么时候登记消息呢,如果登记消息过程失败了怎么办,事务要不要回滚,如果登记成功后,接下来的事务操作失败了怎么办,消息又已经登记了?

1、解决方案1

如果我们消费程序和微服务所连的数据库是同一个数据库,并且消息之间有通过数据库关联,那么只要消息登记失败,就抛出异常报错回滚即可,这种情况只适用于如下业务逻辑:

  1. 生产者在数据库中插入一条消息流水,然后登记消息
  2. 消费者根据消息流水从数据库获取判断消息是否存在,若存在则处理

当消息登记失败后,数据库回滚,那么对消费者没有收到消息,正常。
当下消息登记成功后,数据库操作失败,回滚,那么因为数据库事务回滚了,消费者根据消息流水去数据库中查不到消息,则不处理,正常。

注:这种情况其实还有一个问题,那就是消息登记成功,然后消费者接收到消息,但是因为生产者还没有提交事务,所以消费者这边根据流水号去数据库中是查不到记录的,此时可能要做一些轮询查询,比如循环10次,每次休眠200毫秒,如果10次都查不到就表明没有这条消息,对性能有一定的浪费。

但是,加入我们消费者跟数据库完全没有关系,或者跟生产者连接的不是统一个数据库呢,这种在分布式环境下是很正常的,那怎么办呢?

2、解决方案2

我们可以把消息登记逻辑提取出来,统一在Controller层来登记,那么这种情况下也不会出现解决方案1标注的那种等待数据库事务提交的问题了,也可以保证是数据库提交成功后才会登记消息,完美!

有人就会问,如果消息登记失败怎么办?

我们在数据库事务提交后,然后service层返回要登记的消息到controller层,然后controller层执行登记逻辑,这种情况下登记失败的概率是很低的,但是概率低就是万分之一在高并发的情况下还是有可能的,此时如果登记失败,我们就会新开启一个事务(记得是登记失败才开启),然后将登记失败的消息保存到数据库的一个补偿表,然后让一个后台程序轮询补偿表去重新登记消息。那样就完美了?

也许有人又会问?如果登记到补偿表失败了怎么办?

首先登记失败就已经是小概率事件了,然后登记失败了再insert一条补偿记录到数据库也失败就已经是微乎其微的可能性了,就算有我们如果保存到数据失败,那么就需要记录日志,人工干预了~,相信这种解决方案已经可以满足基本的互联网业务了。

3、事务消息

RocketMQ支持事务消息,只有事务提交后,消费端才能查询到消息。我觉得事务消息实现起来没有解决方案2的补偿模式简单,虽然都是为了最终的一致性。

 196

啊!这个可能是世界上最丑的留言输入框功能~


当然,也是最丑的留言列表

有疑问发邮件到 : suibibk@qq.com 侵权立删
Copyright : 个人随笔   备案号 : 粤ICP备18099399号-2