前言
这两天看了龙果学院的微服务架构的分布式事务解决方案,其中提到了《可靠消息最终一致性方案(本地消息)》,不过课程虽然讲解的详细,但是PPT里面的插图和描述做的不太好,为了之后复习,做一个笔记。
正文
在分布式事务中,有很多诸如CAP,BASE等理论,还有2PC,TCC等解决方案,这些我都不会在这里讲,直接进入主题。
先假设一个场景,我们在下单买东西的时候,需要扣库存,比如说你买了一台电脑,则这台电脑在库存中的数量就应该减1,其实上现在分布式的事务很多都是不需强一致性的,只需要满足最终一致性,像这个场景满足最终一致性就可以了,你买了一台电脑,在一段时间内这台电脑的库存会减1,不要求你刚下单就要马上减库存。
按照微服务的尿性,上述场景涉及到订单服务的下单操作和库存服务扣库存操作,并且涉及到订单库和库存库这两个数据库,所以不能用本地事务解决问题。
下面祭出这张图,是我重新画的,在龙果的基础上加上了标号,这样子可以更好地去描述整个可靠消息最终一致性方案。其中可以把订单服务看作主动方应用系统,库存服务看作被动方应用系统。
- 下单,将订单业务数据存入订单库中
- 存储该订单对应的消息数据(可能只是存一下商品的id和数量,好让库存扣减并发送),消息数据与订单库为同一库,1和2为一个本地事务,要么都成功,要么都失败。
- 消息中间件发送消息到消费端应用。
- 消息消费端应用调用库存服务进行业务操作。
- 返回业务处理结果。
- 消息消费端应用接收到被动方应用的结果后,向消息中间件ack,消息中间件则删除此条消息,当然也可以将此条消息标记为完成。
- 消息消费端应用向主动方应用进行消息确认,让主动方应用删除消息数据。此时一个正常的流程就结束了。
- 消息恢复系统定时主动查询消息数据,看看有没有超过某个时间段还未执行完成的消息(就当作中途失败了)
- 当在第8步查询到有超时的消息数据,则将此消息再次发送给消息中间件,流程相当于再从第3步开始。
再来看看有错(比如说网络断了或者服务器挂了)的时候,这个系统是怎么保证一致性的。
当第1步或者第2步出错的时候,事务回滚,相当于什么都没执行。
当第3、4、5、6、7任意一步出错的时候,都不可能进行消息确认进而删除事先第2步存储下来的消息数据,消息恢复系统会定时去查询消息数据,看看有没有超时的消息数据(比如说超过2分钟还未被删除的),如果有会再次发送给消息中间件,然后又进入步骤3。
不过还得考虑消息被重复消费的情况,比如库存已经扣减了,但是在第5步返回结果的时候网络断了,这样子就不会进行消息确认,后面消息恢复系统又会重发该条消息,这样子这条消息就会被被动方应用重复消费,比如说又扣了一次库存,这样子肯定是不行的,随意被动方应用一定要设计成是幂等操作的,所谓幂等操作就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次调用而产生了副作用。
所以上面总结起来其实就是只要消息数据持久化了,我就假设后面一定会被消费,就算后面挂了一堆东西,但是我们把挂掉的服务再全部启动,这条消息还是会被消费,不会丢失,可以保证最终一致性。
优点
- 消息时效性比较高。
- 从应用设计开发的角度实现了消息数据的可靠性,消息数据的可靠性不依赖于消息中间件,弱化了对MQ中间件特性的依赖。
- 方案轻量,容易实现。
缺点
- 与具体的业务场景绑定,耦合性强,不可公用。
- 消息数据与业务数据同库,占用业务系统资源。
- 业务系统在使用关系型数据库的情况下,消息服务性能会受到关系型数据库并发性能的局限。