消息中间件(Kafka)+HttpClient实现分布式事务详解

业务场景介绍

单体系统下单

在单体的电商系统中,用户下单成功,由于整个下单操作在同一方法中完成,并且商品对应的表和订单对应的表都在同一个数据库中,按照数据库的原子性(Atomicity )、一致性( Consistency )、隔离性或独立性( Isolation)和持久性(Durabilily)特性。下单服务成功后,扣减库存,生成订单操作的数据会同时生效。流程如下:

分布式系统下单

在分布式系统中,将下单过程进行微服务化,下单过程拆分为订单服务和商品服务,此时,数据库也进行拆库,划分为对应的订单数据库和商品数据库。如果,直接按照如上的流程原来的同一事务中的方法替换为对应的微服务,流程图如下:

 在下单服务中使用RPC或者HTTP请求调用商品服务和订单服务时,可能会因为网络异常或者服务器宕机,造成商品数据与订单数据库数据不一致。可能存在订单服务由于调用商品时,因为异常数据回滚在订单数据库中没有生成数据,而商品服务处理成功了商品数据库中的库存扣减成功。网络异常或者服务器宕机出现的原因示例图如下:

分布式事务

数据库的两阶段提交

如何解决如上直接拆分为微服务后,存在的数据不一致的问题呢?这时,就需要引入分布式事务了。如果,要让库存扣减和订单的生成在分布式系统中同时生效,可以通过数据库的两阶段提交方式实现,根据CAP理论(详细可以参考深入理解CAP理论和适用场景)2PC属于CP模式

对数据库分布式事务有了解的同学一定知道数据库支持的2PC,又叫做 XA Transactions。其中,XA 是一个两阶段提交协议,该协议分为以下两个阶段:

  • 第一阶段:事务协调器要求每个涉及到事务的数据库预提交(precommit)此操作,并反映是否可以提交。
  • 第二阶段:事务协调器要求每个数据库提交数据。

其中,如果有任何一个数据库否决此次提交,那么所有数据库操作的数据都会回滚。此种方式会有严重的性能影响,并且也有如下几个缺点:

  1. 同步阻塞问题!执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源也都处于阻塞状态。
  2. 单点故障!由于协调者的重要性,一旦协调者发生故障,参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。
  3. 数据不一致!在二阶段提交的过程中,当协调者向参与者发送commit请求之后,发生了网络异常或者服务器宕机,这会导致部分数据提交成功,部分提交失败,造成数据不一致。

消息中间件+HttpClient实现分布式事务

在分布式系统中,一般更加重视系统的可用性,基于此人们提出了BASE理论,它是用来对CAP定理进行进一步扩充的。BASE理论指的是:

  • Basically Available(基本可用)
  • Soft state(软状态)
  • Eventually consistent(最终一致性)

BASE理论是对CAP中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。在项目中,使用Kafka+HttpClient基于BASE理论实现了分布式事务。实现的思路如下:

  • 服务调用发起方通过Kafka消息发送被调用服务的http信息,以及回调服务调用发起方的success和fail服务信息。此时,服务调用发起方的业务对象状态使用软状态,例如,订单状态为下单中。
  • 创建转发http请求消息中间件的消费者,用于请求被调用服务的http请求。被调用服务执行成功后,根据http请求返回的状态,决定回调服务调用发起方的success服务或fail服务,让服务调用发起方业务对象的状态达到最终一致性,例如,下单成功或下单失败。
  • 为了保证消息被消费,需要在发送消息时采用同步发送的策略。在消费http请求信息时,消息需要确认消费以后,手动提交分区偏移量,保证了消息一定被消费。此时,被调用方的http请求服务,以及服务发起者的success和fail服务都需要满足幂等性设计

以下单服务为例,流程图如下:

其余实现方式可以参考聊聊分布式事务,再说说解决方案

发布了38 篇原创文章 · 获赞 2 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/new_com/article/details/105477594