要说哪种分布式事务处理方案效率高,必然绕不开消息中间件!基于消息中间件的两阶段提交方案,通常用在高并发场景下。这种方式通过牺牲数据的强一致性换取性能的大幅提升,不过实现这种方式的成本和复杂度是比较高的,使用时还要看实际业务情况。
今天小编想通过一个简单的案例,来和大家聊一聊如何通过消息中间件来处理分布式事务。
1. 思路分析
先来说说整体思路。
有一个名词叫做消息驱动的微服务,相信很多小伙伴都听说过。怎么理解呢?
在微服务系统中,服务之间的互相调用,我们可以使用 HTTP 的方式,例如 OpenFeign,也可以使用 RPC 的方式,例如 Dubbo,除了这些方案之外,我们也可以使用消息驱动,这是一种典型的响应式系统设计方案。
在消息驱动的微服务中,服务之间不再互相直接调用,当服务之间需要通信时,就把通信内容发送到消息中间件上,另一个服务则通过监听消息中间件中的消息队列,来完成相应的业务逻辑调用,过程就是这么个过程,并不难,具体怎么玩,我们继续往下看。
2. 业务分析
折腾了半天,后来小编在网上找到了一个别人写好的例子,我觉得用来演示这个问题特别合适,所以我就没有自己写案例了,直接用别人的代码,我们来逐个分析,跟前面讲分布式事务 Seata 的方式一致。
首先我们来看如下一张流程图,这是一个用户购票的案例:
当用户想要购买一张票时:
- 向新订单队列中写入一条数据。
- Order Service 负责消费这个队列中的消息,完成订单的创建,然后再向新订单缴费队列中写入一条消息。
- User Service 负责消费新订单缴费队列中的消息,在 User Service 中完成对用户账户余额的划扣,然后向新订单转移票队列中写入一条消息。
- Ticket Service 负责消费新订单转移票队列,在 Ticket Service 中完成票的转移,然后发送一条消息给订单完成队列。
- 最后 Order Service 中负责监听订单完成队列,处理完成后的订单。
这就是一个典型的消息驱动微服务,也是一个典型的响应式系统。在这个系统中,一共有三个服务,分别是:
- Order Service
- User Service
- Ticket Service
这三个服务之间不会进行任何形式的直接调用,大家有事都是直接发送到消息中间件,其他服务则从消息中间件中获取自己想要的消息然后进行处理。
具体到我们的实践中,则多了一个检查票是否够用的流程,如下图:
创建订单时,先由 Ticket 服务检查票是否够用,没问题的话再继续发起订单的创建。其他过程我就不说了。
另外还需要注意,在售票系统中,由于每张票都不同,例如每张票可能有座位啥的,因此一张票在数据库中往往是被设计成一条记录。
3. 实践
流程我已经说明白了,接下来我们就来看看具体的代码实践。
3.1 准备数据库
首先我们准备三个数据库,分别是:
- javaboy_order:订单库,用户创建订单等操作,在这个数据库中完成。
- javaboy_ticket:票务库,这个库中保存着所有的票据信息,每一张票都是一条记录,都保存在这个库中。
- javaboy_user:用户库,这里保存着用户的账户余额以及付款记录等信息。
每个库中都有各自对应的表,为了操作方便,这些表不用自己创建,将来等项目启动了,利用 JPA 自动创建即可。
3.2 项目概览
我们先来整体上看下这个项目,私信小编【111】可以下载完整代码:
一共有五个服务:
- eureka:注册中心
- order:订单服务
- service:公共模块
- ticket:票务服务
- user:用户服务
下面分别来说。
3.3 注册中心
有人说,都消息驱动了,还要注册中心干嘛?
消息驱动没错,消息驱动微服务之后每个服务只管把消息往消息中间件上扔,每个服务又只管消费消息中间件上的消息,这个时候对于服务注册中心似乎不是那么强需要。不过在我们这个案例中,消息驱动主要用来处理事务问题,其他常规需求我们还是用 OpenFeign 来处理,所以这里我们依然需要一个注册中心。
这里的注册中心我就选择常见的 Eureka,省事一些。由于本文主要是和大家聊分布式事务,所以涉及到微服务的东西我就简单介绍下,不会占用过多